✨ flutter_easyLoading: an easy-to-use plugin for Flutter that contains 23 loading animation effects, progress bar displays, and Toast displays. The pure Flutter side implementation, supporting iOS and Android.

✨ Open Source: github.com/kokohuang/f…

preface

Flutter is an open source cross-platform UI framework that Google launched in 2017 to quickly build high-quality native user interfaces on iOS, Android and the Web. Since its release, Flutter has attracted a lot of native App developers and Web developers. Because Flutter is a new star in the cross-platform space, its ecosystem is not perfect yet. I believe that for those of you who are used to native development, I’m sure you don’t feel like you have to find a wheel. For example, this article will talk about how to display Toast or Loading boxes easily and conveniently in the Flutter application.

explore

At first, I also found several excellent plugins on pub:

  • FlutterToast: This plugin should be a lot just into the pitFlutterIt relies on native, but for UI-level problems, it is best to resolve them on the Flutter side for easy maintenance and to reduce compatibility issues.
  • flutter_oktoast: pureFlutterEnd implementation, convenient call. But the lack ofloadingProgress bar display, can still be customized implementation;

After trial, we found that these plug-ins could not meet our product needs more or less, so WE combined our product needs to build such a wheel, also hope to help students in need. Effect preview:

implementation

ShowDialog implementation

ShowDialog = showDialog = showDialog

Future<T> showDialog<T>({
  @required BuildContext context,
  bool barrierDismissible = true.@Deprecated(
    'Instead of using the "child" argument, return the child from a closure '
    'provided to the "builder" argument. This will ensure that the BuildContext '
    'is appropriate for widgets built in the dialog. '
    'This feature was deprecated after v0.2.3.'
  )
  Widget child,
  WidgetBuilder builder,
  bool useRootNavigator = true,})Copy the code

Context is a mandatory parameter. Those of you who have been involved in Flutter development for a while will know about BuildContext. To put it simply, BuildContext is building the application context in widgets and is an important part of Flutter. BuildContext only appears in two places:

  • StatelessWidget.buildMethod: CreateStatelessWidgetthebuildmethods
  • StateObject: CreateStatefulWidgettheStateThe object’sbuildAnd the other one of the methods isStateMember variable of

A more in-depth discussion of BuildContext is beyond the scope of this article. If we use showDialog to implement popovers, the question is how to get BuildContext anywhere quickly and easily to implement popovers. For those of you who happen to use showDialog, I’m sure you’ll find that getting BuildContext anywhere is not that easy and will generate a lot of unnecessary code.

So, are we left with an experience that is extremely unfriendly?

Of course not. Keep reading.

Flutter EasyLoading introduction

EasyLoading is a simple and easy to use plugin for Flutter, including 23 loading animation effects, progress bar display, Toast display. Pure Flutter side implementation, good compatibility, support iOS and Android. Take a quick look at how to use Flutter EasyLoading.

The installation

Add the following code to the pubspec.yaml file in your project:

dependencies:
  flutter_easyloading: ^ 1.1.0 // Please use the latest version
Copy the code

The import

import 'package:flutter_easyloading/flutter_easyloading.dart';
Copy the code

How to use

First, wrap your App component with the FlutterEasyLoading component:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    /// The subcomponent is usually [MaterialApp] or [CupertinoApp].
    /// This is done to ensure that loading components can be overlaid on top of other components.
    return FlutterEasyLoading(
      child: MaterialApp(
        title: 'Flutter EasyLoading',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: MyHomePage(title: 'Flutter EasyLoading'),),); }}Copy the code

Then, feel free to use it:

EasyLoading.show(status: 'loading... '); 

EasyLoading.showProgress(0.3, status: 'downloading... ');

EasyLoading.showSuccess('Great Success! ');

EasyLoading.showError('Failed with Error');

EasyLoading.showInfo('Useful Information.');

EasyLoading.dismiss();
Copy the code

Custom styles

First, let’s take a look at the custom properties that Flutter EasyLoading currently supports:

/// Loading style, default [easyloadingstyle.dark].
EasyLoadingStyle loadingStyle;

/// Loading type indicator, the default [EasyLoadingIndicatorType fadingCircle].
EasyLoadingIndicatorType indicatorType;

/// Mask type for loading. The default is [easyloadingMaskType.None].
EasyLoadingMaskType maskType;

/// Alignment of text, default [textalign.center].
TextAlign textAlign;

/// Loading The inner margin of the content area.
EdgeInsets contentPadding;

/// The inside margin of the text.
EdgeInsets textPadding;

/// The size of the indicator, default 40.0.
double indicatorSize;

/// Loading fillet size, 5.0 by default.
double radius;

/// Text size, default 15.0.
double fontSize;

/// Width of progress bar indicator, default 2.0.
double progressWidth;

/// [showSuccess] [showError] [showInfo] specifies the display time. The default value is 2000ms.
Duration displayDuration;

/// Color of text, valid only for [easyloadingstyle.custom].
Color textColor;

/// Indicator color, valid only for [easyloadingstyle.custom].
Color indicatorColor;

/// Progress bar indicator color, valid only for [easyloadingstyle.custom].
Color progressColor;

/// Loading background color, only valid for [easyloadingstyle.custom].
Color backgroundColor;

/// The background color of mask, only the [EasyLoadingMaskType. Custom] effectively.
Color maskColor;

/// Whether to allow user operations when loading is displayed.
bool userInteractions;

/// Custom components that show success status
Widget successWidget;

/// Shows a custom component in a failed state
Widget errorWidget;

/// A custom component that displays information state
Widget infoWidget;
Copy the code

Because EasyLoading is a global singleton, we can customize its style anywhere:

EasyLoading.instance .. displayDuration =const Duration(milliseconds: 2000).. indicatorType = EasyLoadingIndicatorType.fadingCircle .. loadingStyle = EasyLoadingStyle.dark .. indicatorSize =45.0
  ..radius = 10.0. backgroundColor = Colors.green .. indicatorColor = Colors.yellow .. textColor = Colors.yellow .. maskColor = Colors.blue.withOpacity(0.5);
Copy the code

See the Flutter_spinkit Showcase for more indicator animation types

As you can see, Flutter EasyLoading is fairly easy to integrate and use, and has a wealth of custom styles that will always satisfy you.

Next, let’s look at the code implementation of Flutter EasyLoading.

Implementation of Flutter EasyLoading

This paper will introduce the main implementation process and ideas of Flutter EasyLoading through the following two knowledge points:

  • Overlay,OverlayEntryImplement a global popover
  • CustomPaintwithCanvasCircular progress bar drawing

Overlay, OverlayEntry implement global popover

Let’s look at the official description of overlays:

/// A [Stack] of entries that can be managed independently.
///
/// Overlays let independent child widgets "float" visual elements on top of
/// other widgets by inserting them into the overlay's [Stack]. The overlay lets
/// each of these widgets manage their participation in the overlay using
/// [OverlayEntry] objects.
///
/// Although you can create an [Overlay] directly, it's most common to use the
/// overlay created by the [Navigator] in a [WidgetsApp] or a [MaterialApp]. The
/// navigator uses its overlay to manage the visual appearance of its routes.
///
/// See also:
///
///  * [OverlayEntry].
///  * [OverlayState].
///  * [WidgetsApp].
///  * [MaterialApp].
class Overlay extends StatefulWidget {}
Copy the code

That is, an Overlay is a Widget of the Stack, and you can insert an OverlayEntry into the Overlay to make a separate child window hover over the other widgets. Using this feature, we can Overlay the MaterialApp or CupertinoApp with overlays. The purpose of this is to ensure that the loading component can be overlaid on top of other components. Because there is only one MaterialApp or CupertinoApp root component in a Flutter. (Note: This is a reference to the Flutter_oktoast plugin, thanks).

In addition, the purpose of doing this is to solve another core problem: to cache the context in memory, all subsequent calls do not need to provide the context. The implementation is as follows:

@override
Widget build(BuildContext context) {
  return Directionality(
    child: Overlay(
      initialEntries: [
        OverlayEntry(
          builder: (BuildContext _context) {
            / / cache the context
            EasyLoading.instance.context = _context;
            // The child must be MaterialApp or CupertinoApp
            return widget.child;
          },
        ),
      ],
    ),
    textDirection: widget.textDirection,
  );
}
Copy the code
/ / create OverlayEntry
OverlayEntry _overlayEntry = OverlayEntry(
  builder: (BuildContext context) => LoadingContainer(
    key: _key,
    status: status,
    indicator: w,
    animation: _animation,
  ),
);

// Insert the OverlayEntry into the Overlay
// Use Overlay. Of () to get the Overlay of the App root node
Overlay.of(_getInstance().context).insert(_overlayEntry);

// Call the remove() method of the OverlayEntry itself to remove itself from the Overlay it sits on
_overlayEntry.remove();
Copy the code

Overlay and OverlayEntry are easy to use and understand. We can also use them in more scenarios, such as PopupWindow popovers, global custom Dialog popovers, and so on. As long as we use it flexibly, we can achieve many of the effects we want.

CustomPaintwithCanvasCircular progress bar drawing

Almost all UI systems provide an interface for self-drawing UI. This interface usually provides a 2D Canvas Canvas, which encapsulates some basic drawing APIS. We can draw various customized graphics through the Canvas. In Flutter, a CustomPaint component is provided that combines a brush, CustomPainter, to draw custom graphics. Next I will briefly introduce the implementation of the circular progress bar.

Let’s start with the CustomPaint constructor:

const CustomPaint({
  Key key,
  this.painter,
  this.foregroundPainter,
  this.size = Size.zero,
  this.isComplex = false.this.willChange = false,
  Widget child,
})
Copy the code
  • Painter: Background brush, which is displayed after the child node;
  • ForegroundPainter: foregroundPainter, which is displayed in front of child nodes
  • Size: whenchildfornullRepresents the default drawing area size, if anychildThis parameter is ignored and the canvas size ischildSize. If you havechildBut if you want to specify a canvas of a specific size, you can useSizeBoxThe parcelCustomPaintThe implementation.
  • IsComplex: Whether to draw complex, if so,FlutterSome caching strategies are applied to reduce the overhead of repeated rendering.
  • WillChange:isComplexUsed together, this property indicates whether the drawing will change in the next frame when caching is enabled.

As you can see, when drawing we need to provide a foreground or background brush, or both. Our brushes need to inherit from the CustomPainter class, where we implement the real drawing logic.

Next, let’s see how to draw the circular progress bar with CustomPainter:

class _CirclePainter extends CustomPainter {
  final Color color;
  final double value;
  final double width;

  _CirclePainter({
    @required this.color,
    @required this.value,
    @required this.width,
  });

  @override
  void paint(Canvas canvas, Size size) {
    finalpaint = Paint() .. color = color .. strokeWidth = width .. style = PaintingStyle.stroke .. strokeCap = StrokeCap.round; canvas.drawArc( Offset.zero & size, -math.pi /2,
      math.pi * 2 * value,
      false,
      paint,
    );
  }

  @override
  boolshouldRepaint(_CirclePainter oldDelegate) => value ! = oldDelegate.value; }Copy the code

As you can see from the above, the CustomPainter defines a virtual function paint:

void paint(Canvas canvas, Size size);
Copy the code

This function is the heart of drawing and takes the following two arguments:

  • Canvas: a canvas that includes various drawing methods, such asDrawLine draw a line (),DrawRect (draw rectangle),Methods like drawCircle (circle)Etc.
  • Size: indicates the size of the current drawing area

Now that we have the canvas, we need a brush. Flutter provides the Paint class to implement brushes. In addition, you can configure various brush properties such as thickness, color, style, etc., for example:

finalpaint = Paint() .. color = color/ / color
  ..strokeWidth = width / / width. style = PaintingStyle.stroke .. strokeCap = StrokeCap.round;Copy the code

Finally, we need to use drawArc method for arc drawing:

canvas.drawArc(
  Offset.zero & size,
  -math.pi / 2,
  math.pi * 2 * value,
  false,
  paint,
);
Copy the code

At this point, we have completed the progress bar drawing. We also need to pay attention to drawing performance issues. Fortunately, the class provides a shouldRepaint method that determines when the canvas should be redrawn, which can be quite effective for improving performance in complex drawings.

@override
boolshouldRepaint(_CirclePainter oldDelegate) => value ! = oldDelegate.value;Copy the code

conclusion

There is no doubt that Flutter has a bright future. Maybe there are still many problems, but I believe that more people will be willing to grow up with Flutter. Looking forward to the completion of the Flutter ecosphere. I will also gradually improve Flutter EasyLoading in the later stage, looking forward to your valuable suggestions.

Finally, HOPEFULLY Flutter EasyLoading will be helpful to you.