1 Star 0 Fork 0

cui / flutter_smart_dialog

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
MIT

flutter_smart_dialog

语言: English | 中文简体

An elegant Flutter Dialog solution.

Preface

The Dialog that comes with the system actually pushes a new page, which has many benefits, but there are also some difficult problems to solve

  • Must pass BuildContext
    • Loading pop-ups are usually encapsulated in the network framework, and it is a headache to pass more context parameters; it is good to use fish_redux, the effect layer can directly get the context, if you use bloc, you have to pass the context to bloc or cubit in the view layer . . .
  • Cannot penetrate dark background, click on the page behind dialog
    • This is a real headache. I have thought of a lot of ways, but failed to solve this problem on the built-in dialog.
  • The loading pop-up window written by Dialog comes with the system. In the case of network requests and page jumps, there will be routing confusion
    • Scenario review: The loading library is generally encapsulated in the network layer. After a page is submitted, the page needs to be jumped. The submission operation is completed and the page jumps. The loading is closed in an asynchronous callback (onError or onSuccess), and it will appear When the jump operation is performed, the pop-up window has not been closed, and will be closed after a short delay, because the pop page method is used, and the jumped page will be popped.
    • The above is a very common scenario. It is more difficult to predict when it comes to complex scenarios. There are also solutions: locate whether the top of the page stack is the Loading pop-up window, select Pop, and troublesome implementation

The above pain points are all fatal, of course, there are some other solutions, such as:

  • Use Stack at the top of the page
  • Use Overlay

Obviously, the use of Overlay is the most portable. At present, many toast and dialog three-party libraries use this solution. Some loading libraries are used. After reading the source code, the penetration background solution is very different from the expected effect. The dialog library comes with toast display, but toast display cannot coexist with dialog (toast is a special information display and should be able to exist independently), so I need to rely on one more Toast library

SmartDialog

Based on the above difficult problems, I can only implement it myself. It took some time to implement a Pub package. Basically, the pain points that should be solved have been solved, and there is no problem in actual business

Effect

smartDialog

Introduction

dependencies:
  flutter_smart_dialog: any

Use

  • Main entrance configuration
    • It needs to be configured in the main entrance, so that you can use Dialog without passing BuildContext
    • You only need to configure it in the builder parameters of MaterialApp
void main() {
  runApp(MyApp());
}

///flutter 2.0
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Container(),
      builder: (BuildContext context, Widget? child) {
        return FlutterSmartDialog(child: child);
      },
    );
  }
}

///flutter 1.x
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Container(),
      builder: (BuildContext context, Widget child) {
        return FlutterSmartDialog(child: child);
      },
    );
  }
}

Use FlutterSmartDialog to wrap the child, and then you can use SmartDialog happily

  • Use Toast: Because of the special nature of toast, some optimizations have been made to toast separately here
    • time: optional, Duration type, default 1500ms
    • isDefaultDismissType: the type of toast disappearing, the default is true
      • true: default disappearing type, similar to android toast, toast displayed one by one
      • false: non-default disappearing type, multiple clicks, the toast will be displayed after the former toast
    • widget: toast can be customized
    • msg: mandatory message
    • alignment: optional, control toast position
    • If you want to use the fancy Toast effect, please use the showToast method to customize it. Fried chicken is simple, too lazy to write it yourself. Just copy my ToastWidget and change the attributes.
SmartDialog.showToast('test toast');
  • Use Loading: Loading has many setting properties, please refer to the SmartDialog configuration parameter description below
    • msg: optional, the text message below the loading animation (default: loading...)
//open loading
SmartDialog.showLoading();

//delay off
await Future.delayed(Duration(seconds: 2));
SmartDialog.dismiss();
  • Custom dialog
    • Use the SmartDialog.show() method, which contains many parameters with a suffix of Temp, which are consistent with the following parameters without the suffix of Temp
    • Special attribute isUseExtraWidget: whether to use an additional overlay floating layer, it can be separated from the main floating layer; it can be separated from loading, dialog, etc. The built-in showToast turns on this configuration and can coexist with loading
SmartDialog.show(
    alignmentTemp: Alignment.bottomCenter,
    clickBgDismissTemp: true,
    widget: Container(
      color: Colors.blue,
      height: 300,
    ),
);
  • SmartDialog configuration parameter description
    • In order to avoid exposing too many attributes in instance, causing inconvenience to use, many parameters here are managed by config attribute in instance
    • The attributes set by config are global. Using Config to manage these attributes is to facilitate the modification and management of these attributes, and also to make the SmartDialog class easier to maintain
Parameters Function description
alignment Control the position of the custom control on the screen
Alignment.center: The custom control is located in the middle of the screen, and the animation defaults to: fade and zoom, you can use isLoading to select the animation
Alignment.bottomCenter, Alignment.bottomLeft, Alignment.bottomRight: The custom control is located at the bottom of the screen, the animation defaults to displacement animation, bottom-up, you can use animationDuration to set the animation time
Alignment.topCenter, Alignment.topLeft, Alignment.topRight: The custom control is located at the top of the screen, and the animation defaults to displacement animation. From top to bottom, you can use animationDuration to set the animation time
Alignment.centerLeft: The custom control is located at On the left side of the screen, the animation defaults to displacement animation, from left to right, you can use animationDuration to set the animation time
Alignment.centerRight: The custom control is located on the left side of the screen, and the animation defaults to displacement animation, from right to left. You can use animationDuration to set the animation time
isPenetrate Default: false; whether to penetrate the background of the mask, control after interactive mask, true: click to penetrate the background, false: not penetrate; set the penetration mask to true, the background mask will automatically become transparent (Required)
clickBgDismiss Default: true; click the mask, whether to close the dialog---true: click the mask to close the dialog, false: not close
maskColor Mask color
animationDuration Animation time
isUseAnimation Default: true; whether to use animation
isLoading Default: true; whether to use Loading animation; true: use fade animation for content body false: use zoom animation for content body, only for the middle position control
isExist Default: false; Whether the main SmartDialog (OverlayEntry) exists on the interface
isExistExtra Default: false; whether additional SmartDialog (OverlayEntry) exists on the interface
  • Use of Config attribute, for example
    • The relevant attributes have been initialized internally; if you need to customize, you can initialize the attributes you want at the main entrance
SmartDialog.instance.config
    ..clickBgDismiss = true
    ..isLoading = true
    ..isUseAnimation = true
    ..animationDuration = Duration(milliseconds: 270)
    ..isPenetrate = false
    ..maskColor = Colors.black.withOpacity(0.1)
    ..alignment = Alignment.center;
  • Return to the event, close the pop-up window solution

There is basically a problem when using Overlay's dependency library. It is difficult to monitor the return event, which makes it difficult to close the pop-up window layout when the return event is violated. After thinking of many ways, there is no way to solve the problem in the dependency library. Here is one BaseScaffold, use BaseScaffold on each page to solve the problem of closing Dialog in return event

  • Flutter 2.0
typedef ScaffoldParamVoidCallback = void Function();

class BaseScaffold extends StatefulWidget {
  const BaseScaffold({
    Key? key,
    this.appBar,
    this.body,
    this.floatingActionButton,
    this.floatingActionButtonLocation,
    this.floatingActionButtonAnimator,
    this.persistentFooterButtons,
    this.drawer,
    this.endDrawer,
    this.bottomNavigationBar,
    this.bottomSheet,
    this.backgroundColor,
    this.resizeToAvoidBottomInset,
    this.primary = true,
    this.drawerDragStartBehavior = DragStartBehavior.start,
    this.extendBody = false,
    this.extendBodyBehindAppBar = false,
    this.drawerScrimColor,
    this.drawerEdgeDragWidth,
    this.drawerEnableOpenDragGesture = true,
    this.endDrawerEnableOpenDragGesture = true,
    this.isTwiceBack = false,
    this.isCanBack = true,
    this.onBack,
  })  : assert(primary != null),
        assert(extendBody != null),
        assert(extendBodyBehindAppBar != null),
        assert(drawerDragStartBehavior != null),
        super(key: key);

  final bool extendBody;
  final bool extendBodyBehindAppBar;
  final PreferredSizeWidget? appBar;
  final Widget? body;
  final Widget? floatingActionButton;
  final FloatingActionButtonLocation? floatingActionButtonLocation;
  final FloatingActionButtonAnimator? floatingActionButtonAnimator;
  final List<Widget>? persistentFooterButtons;
  final Widget? drawer;
  final Widget? endDrawer;
  final Color? drawerScrimColor;
  final Color? backgroundColor;
  final Widget? bottomNavigationBar;
  final Widget? bottomSheet;
  final bool? resizeToAvoidBottomInset;
  final bool primary;
  final DragStartBehavior drawerDragStartBehavior;
  final double? drawerEdgeDragWidth;
  final bool drawerEnableOpenDragGesture;
  final bool endDrawerEnableOpenDragGesture;

  ///增加的属性
  ///点击返回按钮提示是否退出页面,快速点击俩次才会退出页面
  final bool isTwiceBack;

  ///是否可以返回
  final bool isCanBack;

  ///监听返回事件
  final ScaffoldParamVoidCallback? onBack;

  @override
  _BaseScaffoldState createState() => _BaseScaffoldState();
}

class _BaseScaffoldState extends State<BaseScaffold> {
  DateTime? _lastPressedAt;

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      child: Scaffold(
        appBar: widget.appBar,
        body: widget.body,
        floatingActionButton: widget.floatingActionButton,
        floatingActionButtonLocation: widget.floatingActionButtonLocation,
        floatingActionButtonAnimator: widget.floatingActionButtonAnimator,
        persistentFooterButtons: widget.persistentFooterButtons,
        drawer: widget.drawer,
        endDrawer: widget.endDrawer,
        bottomNavigationBar: widget.bottomNavigationBar,
        bottomSheet: widget.bottomSheet,
        backgroundColor: widget.backgroundColor,
        resizeToAvoidBottomInset: widget.resizeToAvoidBottomInset,
        primary: widget.primary,
        drawerDragStartBehavior: widget.drawerDragStartBehavior,
        extendBody: widget.extendBody,
        extendBodyBehindAppBar: widget.extendBodyBehindAppBar,
        drawerScrimColor: widget.drawerScrimColor,
        drawerEdgeDragWidth: widget.drawerEdgeDragWidth,
        drawerEnableOpenDragGesture: widget.drawerEnableOpenDragGesture,
        endDrawerEnableOpenDragGesture: widget.endDrawerEnableOpenDragGesture,
      ),
      onWillPop: dealWillPop,
    );
  }

  Future<bool> dealWillPop() async {
    if (widget.onBack != null) {
      widget.onBack!();
    }

    if (SmartDialog.instance.config.isExist) {
      SmartDialog.dismiss();
      return false;
    }

    if (!widget.isCanBack) {
      return false;
    }

    if (widget.isTwiceBack) {
      if (_lastPressedAt == null ||
          DateTime.now().difference(_lastPressedAt!) > Duration(seconds: 1)) {
        _lastPressedAt = DateTime.now();

        SmartDialog.showToast("Click again to exit");
        return false;
      }
      return true;
    } else {
      return true;
    }
  }
}
  • Flutter 1.x
typedef ScaffoldParamVoidCallback = void Function();

class BaseScaffold extends StatefulWidget {
  const BaseScaffold({
    Key key,
    this.appBar,
    this.body,
    this.floatingActionButton,
    this.floatingActionButtonLocation,
    this.floatingActionButtonAnimator,
    this.persistentFooterButtons,
    this.drawer,
    this.endDrawer,
    this.bottomNavigationBar,
    this.bottomSheet,
    this.backgroundColor,
    this.resizeToAvoidBottomInset,
    this.primary = true,
    this.drawerDragStartBehavior = DragStartBehavior.start,
    this.extendBody = false,
    this.extendBodyBehindAppBar = false,
    this.drawerScrimColor,
    this.drawerEdgeDragWidth,
    this.drawerEnableOpenDragGesture = true,
    this.endDrawerEnableOpenDragGesture = true,
    this.isTwiceBack = false,
    this.isCanBack = true,
    this.onBack,
  })  : assert(primary != null),
        assert(extendBody != null),
        assert(extendBodyBehindAppBar != null),
        assert(drawerDragStartBehavior != null),
        super(key: key);

  final bool extendBody;
  final bool extendBodyBehindAppBar;
  final PreferredSizeWidget appBar;
  final Widget body;
  final Widget floatingActionButton;
  final FloatingActionButtonLocation floatingActionButtonLocation;
  final FloatingActionButtonAnimator floatingActionButtonAnimator;
  final List<Widget> persistentFooterButtons;
  final Widget drawer;
  final Widget endDrawer;
  final Color drawerScrimColor;
  final Color backgroundColor;
  final Widget bottomNavigationBar;
  final Widget bottomSheet;
  final bool resizeToAvoidBottomInset;
  final bool primary;
  final DragStartBehavior drawerDragStartBehavior;
  final double drawerEdgeDragWidth;
  final bool drawerEnableOpenDragGesture;
  final bool endDrawerEnableOpenDragGesture;

  ///增加的属性
  ///点击返回按钮提示是否退出页面,快速点击俩次才会退出页面
  final bool isTwiceBack;

  ///是否可以返回
  final bool isCanBack;

  ///监听返回事件
  final ScaffoldParamVoidCallback onBack;

  @override
  _BaseScaffoldState createState() => _BaseScaffoldState();
}

class _BaseScaffoldState extends State<BaseScaffold> {
  DateTime _lastPressedAt;

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      child: Scaffold(
        appBar: widget.appBar,
        body: widget.body,
        floatingActionButton: widget.floatingActionButton,
        floatingActionButtonLocation: widget.floatingActionButtonLocation,
        floatingActionButtonAnimator: widget.floatingActionButtonAnimator,
        persistentFooterButtons: widget.persistentFooterButtons,
        drawer: widget.drawer,
        endDrawer: widget.endDrawer,
        bottomNavigationBar: widget.bottomNavigationBar,
        bottomSheet: widget.bottomSheet,
        backgroundColor: widget.backgroundColor,
        resizeToAvoidBottomInset: widget.resizeToAvoidBottomInset,
        primary: widget.primary,
        drawerDragStartBehavior: widget.drawerDragStartBehavior,
        extendBody: widget.extendBody,
        extendBodyBehindAppBar: widget.extendBodyBehindAppBar,
        drawerScrimColor: widget.drawerScrimColor,
        drawerEdgeDragWidth: widget.drawerEdgeDragWidth,
        drawerEnableOpenDragGesture: widget.drawerEnableOpenDragGesture,
        endDrawerEnableOpenDragGesture: widget.endDrawerEnableOpenDragGesture,
      ),
      onWillPop: dealWillPop,
    );
  }

  Future<bool> dealWillPop() async {
    if (widget.onBack != null) {
      widget.onBack();
    }

    if (SmartDialog.instance.config.isExist) {
      SmartDialog.dismiss();
      return false;
    }

    if (!widget.isCanBack) {
      return false;
    }

    if (widget.isTwiceBack) {
      if (_lastPressedAt == null ||
          DateTime.now().difference(_lastPressedAt) > Duration(seconds: 1)) {
        _lastPressedAt = DateTime.now();

        SmartDialog.showToast("Click again to exit");
        return false;
      }
      return true;
    } else {
      return true;
    }
  }
}
MIT License Copyright (c) 2020 kokohuang Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR AyNY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

简介

暂无描述 展开 收起
MIT
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/cuixingrui/flutter_smart_dialog.git
git@gitee.com:cuixingrui/flutter_smart_dialog.git
cuixingrui
flutter_smart_dialog
flutter_smart_dialog
master

搜索帮助