diff --git a/CHANGELOG.md b/CHANGELOG.md index a46db45..72b25ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ * break change: "AlignmentGeometry" adjust to "Alignment" * fix [#185](https://github.com/fluttercandies/flutter_smart_dialog/issues/185) * optimize bindWidget, when bindWidget is not null, bindPage will be automatically set to false. +* Reconstruct the implementation of initialization (fix [#109](https://github.com/fluttercandies/flutter_smart_dialog/issues/109), [#183](https://github.com/fluttercandies/flutter_smart_dialog/issues/183)) + # [4.9.0] diff --git a/example/lib/demo/issue181_overstep.dart b/example/lib/demo/issue181_overstep.dart index ac9729e..fe26214 100644 --- a/example/lib/demo/issue181_overstep.dart +++ b/example/lib/demo/issue181_overstep.dart @@ -101,7 +101,7 @@ class _TestingDialogState extends State<_TestingDialog> InkWell( onTap: () { SmartDialog.dismiss(); - print("111111111111"); + debugPrint("111111111111"); }, child: const Row(children: [ Icon(Icons.close), @@ -111,7 +111,7 @@ class _TestingDialogState extends State<_TestingDialog> InkWell( onTap: () { SmartDialog.dismiss(); - print("2222222222222"); + debugPrint("2222222222222"); }, child: const Row(children: [ Icon(Icons.close), diff --git a/example/lib/demo/issue183_visibility.dart b/example/lib/demo/issue183_visibility.dart new file mode 100644 index 0000000..11b0bd6 --- /dev/null +++ b/example/lib/demo/issue183_visibility.dart @@ -0,0 +1,144 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:visibility_detector/visibility_detector.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + theme: ThemeData( + colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), + useMaterial3: true, + ), + home: const MyHomePage(title: 'Flutter Demo Home Page'), + navigatorObservers: [FlutterSmartDialog.observer], + builder: FlutterSmartDialog.init(), //这里注释掉就不会有问题 + ); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({super.key, required this.title}); + + final String title; + + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + final int _counter = 0; + + void _incrementCounter() { + Navigator.push( + context, + MaterialPageRoute(builder: (context) => const Page1()), + ); + } + + @override + Widget build(BuildContext context) { + debugPrint("rebuild"); + // This method is rerun every time setState is called, for instance as done + // by the _incrementCounter method above. + // + // The Flutter framework has been optimized to make rerunning build methods + // fast, so that you can just rebuild anything that needs updating rather + // than having to individually change instances of widgets. + return Scaffold( + appBar: AppBar( + // TRY THIS: Try changing the color here to a specific color (to + // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar + // change color while the other colors stay the same. + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + // Here we take the value from the MyHomePage object that was created by + // the App.build method, and use it to set our appbar title. + title: Text(widget.title), + ), + body: Column( + children: [ + Expanded( + child: VisibilityDetector( + key: const Key("rec"), + onVisibilityChanged: (VisibilityInfo visibilityInfo) { + var visiblePercentage = visibilityInfo.visibleFraction * 100; + if (visiblePercentage == 100.0) { + debugPrint("推荐页面111"); + } + }, + child: PageView( + physics: const NeverScrollableScrollPhysics(), + children: [ + VisibilityDetector( + key: const Key("reca"), + onVisibilityChanged: (VisibilityInfo visibilityInfo) { + var visiblePercentage = + visibilityInfo.visibleFraction * 100; + if (visiblePercentage == 100.0) { + debugPrint("首页首页"); + } + }, + child: SizedBox( + width: 300, + // color: Colors.red, + child: Column( + children: [ + const Text( + 'You have pushed the button this many times:', + ), + InkWell( + onTap: () { + _incrementCounter(); + }, + child: const Text("dfdfd"), + ) + ], + ), + ), + ), + Text( + '$_counter', + style: Theme.of(context).textTheme.headlineMedium, + ), + ], + ), + ), + ), + Container(width: 200, height: 40, color: Colors.red) + ], + ), + floatingActionButton: FloatingActionButton( + onPressed: _incrementCounter, + tooltip: 'Increment', + child: const Icon(Icons.add), + ), // This trailing comma makes auto-formatting nicer for build methods. + ); + } +} + +class Page1 extends StatelessWidget { + const Page1({super.key}); + + @override + Widget build(BuildContext context) { + // TODO: implement build + return PageView( + children: const [ + Center( + child: Text( + "page1", + style: TextStyle(color: Colors.red), + ), + ) + ], + ); + } +} \ No newline at end of file diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 190cd1d..0b04090 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -28,6 +28,8 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.0 + # 用于处理widget是否可见检测 https://pub.flutter-io.cn/packages/visibility_detector + visibility_detector: # mac flutter_smart_dialog: path: ../ diff --git a/lib/src/custom/main_dialog.dart b/lib/src/custom/main_dialog.dart index f7cf429..f738d6c 100644 --- a/lib/src/custom/main_dialog.dart +++ b/lib/src/custom/main_dialog.dart @@ -231,6 +231,8 @@ class MainDialog { awaitOverType: SmartAwaitOverType.dialogDismiss, result: result, ); + + DialogProxy.instance.smartOverlayController.dismiss(); } Widget getWidget() => Offstage(offstage: !visible, child: _widget); diff --git a/lib/src/custom/toast/toast_tool.dart b/lib/src/custom/toast/toast_tool.dart index 6634cb7..aa4238a 100644 --- a/lib/src/custom/toast/toast_tool.dart +++ b/lib/src/custom/toast/toast_tool.dart @@ -23,19 +23,23 @@ class ToastTool { return; } + if (closeAll) { + clearAllToast(); + SmartDialog.config.toast.isExist = false; + return; + } + var curToast = toastQueue.first; + toastQueue.remove(curToast); + if (toastQueue.isEmpty) { + SmartDialog.config.toast.isExist = false; + } + await curToast.mainDialog.dismiss(); - await Future.delayed(SmartDialog.config.toast.intervalTime); if (curToast.mainDialog.overlayEntry.mounted) { curToast.mainDialog.overlayEntry.remove(); } - - toastQueue.remove(curToast); - if (closeAll) { - clearAllToast(); - } - if (toastQueue.length > 1) return; - SmartDialog.config.toast.isExist = false; + await Future.delayed(SmartDialog.config.toast.intervalTime); } void clearAllToast() { diff --git a/lib/src/helper/dialog_proxy.dart b/lib/src/helper/dialog_proxy.dart index 51b5481..73deb51 100644 --- a/lib/src/helper/dialog_proxy.dart +++ b/lib/src/helper/dialog_proxy.dart @@ -15,6 +15,7 @@ import '../config/smart_config.dart'; import '../data/animation_param.dart'; import '../data/notify_info.dart'; import '../init_dialog.dart'; +import '../widget/helper/smart_overlay.dart'; import '../widget/helper/smart_overlay_entry.dart'; enum CloseType { @@ -45,10 +46,10 @@ enum DialogType { class DialogProxy { late SmartConfig config; late SmartOverlayEntry entryLoading; - late SmartOverlayEntry entryNotify; late Queue dialogQueue; late Queue notifyQueue; - late CustomLoading _loading; + late CustomLoading loadingWidget; + late SmartOverlayController smartOverlayController; static DialogProxy? _instance; @@ -71,18 +72,17 @@ class DialogProxy { DialogProxy._internal() { config = SmartConfig(); + smartOverlayController = SmartOverlayController(); dialogQueue = ListQueue(); notifyQueue = ListQueue(); } void initialize(Set initType) { if (initType.contains(SmartInitType.loading)) { - entryLoading = SmartOverlayEntry(builder: (_) => _loading.getWidget()); - _loading = CustomLoading(overlayEntry: entryLoading); - } - - if (initType.contains(SmartInitType.notify)) { - entryNotify = SmartOverlayEntry(builder: (_) => const SizedBox.shrink()); + entryLoading = SmartOverlayEntry(builder: (_) { + return loadingWidget.getWidget(); + }); + loadingWidget = CustomLoading(overlayEntry: entryLoading); } } @@ -110,7 +110,8 @@ class DialogProxy { required bool bindPage, required BuildContext? bindWidget, required Rect? ignoreArea, - }) { + }) async { + await beforeShow(); CustomDialog? dialog; var entry = SmartOverlayEntry( builder: (BuildContext context) => dialog!.getWidget(), @@ -162,7 +163,8 @@ class DialogProxy { required String? tag, required bool keepSingle, required SmartBackType backType, - }) { + }) async { + await beforeShow(); CustomNotify? dialog; var entry = SmartOverlayEntry( builder: (BuildContext context) => dialog!.getWidget(), @@ -219,7 +221,8 @@ class DialogProxy { required bool useSystem, required bool bindPage, required BuildContext? bindWidget, - }) { + }) async { + await beforeShow(); CustomDialog? dialog; var entry = SmartOverlayEntry( builder: (BuildContext context) => dialog!.getWidget(), @@ -273,8 +276,9 @@ class DialogProxy { required Duration? displayTime, required bool backDismiss, required Widget widget, - }) { - return _loading.showLoading( + }) async { + await beforeShow(); + return loadingWidget.showLoading( alignment: alignment, clickMaskDismiss: clickMaskDismiss, animationType: animationType, @@ -311,7 +315,8 @@ class DialogProxy { required bool debounce, required SmartToastType displayType, required Widget widget, - }) { + }) async { + await beforeShow(); CustomToast? toast; var entry = SmartOverlayEntry( builder: (BuildContext context) => toast!.getWidget(), @@ -337,6 +342,10 @@ class DialogProxy { ); } + Future beforeShow() async { + await smartOverlayController.show(); + } + Future? dismiss({ required SmartStatus status, String? tag, @@ -349,7 +358,7 @@ class DialogProxy { if (loading && (tag == null || (dialogQueue.isEmpty && notifyQueue.isEmpty))) { - return _loading.dismiss(closeType: closeType); + return loadingWidget.dismiss(closeType: closeType); } if (notifyQueue.isNotEmpty) { @@ -386,7 +395,7 @@ class DialogProxy { } else if (status == SmartStatus.allToast) { return CustomToast.dismiss(closeAll: true); } else if (status == SmartStatus.loading) { - return _loading.dismiss(closeType: closeType); + return loadingWidget.dismiss(closeType: closeType); } else if (status == SmartStatus.notify || status == SmartStatus.allNotify) { return CustomNotify.dismiss( diff --git a/lib/src/init_dialog.dart b/lib/src/init_dialog.dart index a19b985..511ab4a 100644 --- a/lib/src/init_dialog.dart +++ b/lib/src/init_dialog.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/src/helper/navigator_observer.dart'; @@ -6,6 +5,7 @@ import 'package:flutter_smart_dialog/src/kit/view_utils.dart'; import 'package:flutter_smart_dialog/src/widget/default/notify_alter.dart'; import 'package:flutter_smart_dialog/src/widget/default/notify_error.dart'; import 'package:flutter_smart_dialog/src/widget/default/notify_failure.dart'; +import 'package:flutter_smart_dialog/src/widget/helper/smart_overlay.dart'; import 'helper/dialog_proxy.dart'; import 'helper/pop_monitor/boost_route_monitor.dart'; @@ -28,7 +28,6 @@ class FlutterSmartDialog extends StatefulWidget { this.notifyStyle, this.styleBuilder, this.initType, - this.useDebugModel, }) : super(key: key); final Widget? child; @@ -48,9 +47,6 @@ class FlutterSmartDialog extends StatefulWidget { ///inti type final Set? initType; - ///if you set 'useDebugModel' to true, the SmartDialog function will be closed - final bool? useDebugModel; - @override State createState() => _FlutterSmartDialogState(); @@ -73,8 +69,6 @@ class FlutterSmartDialog extends StatefulWidget { FlutterSmartStyleBuilder? styleBuilder, //init type Set? initType, - //if you set 'useDebugModel' to true, the SmartDialog function will be closed - bool? useDebugModel, }) { MonitorPopRoute.instance; @@ -86,7 +80,6 @@ class FlutterSmartDialog extends StatefulWidget { notifyStyle: notifyStyle, styleBuilder: styleBuilder, initType: initType, - useDebugModel: useDebugModel, child: child, ) : builder( @@ -97,7 +90,6 @@ class FlutterSmartDialog extends StatefulWidget { notifyStyle: notifyStyle, styleBuilder: styleBuilder, initType: initType, - useDebugModel: useDebugModel, child: child, ), ); @@ -108,7 +100,6 @@ class FlutterSmartDialog extends StatefulWidget { class _FlutterSmartDialogState extends State { late FlutterSmartStyleBuilder styleBuilder; late Set initType; - late bool debugModel; @override void initState() { @@ -165,51 +156,21 @@ class _FlutterSmartDialogState extends State { ); } - // debug model - debugModel = (widget.useDebugModel ?? false) && kDebugMode; - super.initState(); } @override Widget build(BuildContext context) { - if (debugModel) { - return styleBuilder(widget.child ?? Container()); - } - - return styleBuilder( - Overlay(initialEntries: [ - //main layout - OverlayEntry( - builder: (BuildContext context) { - if (initType.contains(SmartInitType.custom)) { - DialogProxy.contextCustom = context; - } - - if (initType.contains(SmartInitType.attach)) { - DialogProxy.contextAttach = context; - } - - if (initType.contains(SmartInitType.notify)) { - DialogProxy.contextNotify = context; - } - - if (initType.contains(SmartInitType.toast)) { - DialogProxy.contextToast = context; - } - - return widget.child ?? Container(); - }, - ), - - // if (initType.contains(SmartInitType.notify)) - // DialogProxy.instance.entryNotify, - - //provided separately for loading - if (initType.contains(SmartInitType.loading)) - DialogProxy.instance.entryLoading, - ]), - ); + return styleBuilder(Stack(children: [ + // main widget + widget.child ?? const SizedBox.shrink(), + + // dialog + SmartOverlay( + initType: initType, + controller: DialogProxy.instance.smartOverlayController, + ) + ])); } BuildContext? getNavigatorContext(Navigator navigator) { diff --git a/lib/src/widget/helper/smart_overlay.dart b/lib/src/widget/helper/smart_overlay.dart new file mode 100644 index 0000000..381b1f4 --- /dev/null +++ b/lib/src/widget/helper/smart_overlay.dart @@ -0,0 +1,91 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; + +import '../../../flutter_smart_dialog.dart'; +import '../../helper/dialog_proxy.dart'; +import '../../kit/view_utils.dart'; + +part 'smart_overly_controller.dart'; + +class SmartOverlay extends StatefulWidget { + const SmartOverlay({ + Key? key, + required this.initType, + this.controller, + }) : super(key: key); + + final Set initType; + final SmartOverlayController? controller; + + @override + State createState() => _SmartOverlayState(); +} + +class _SmartOverlayState extends State { + bool visible = false; + + @override + void initState() { + widget.controller?._setListener( + onShow: () { + setState(() => visible = true); + }, + onDismiss: () => widgetsBinding.addPostFrameCallback((_) { + setState(() { + visible = SmartDialog.config.checkExist(dialogTypes: { + SmartAllDialogType.custom, + SmartAllDialogType.attach, + SmartAllDialogType.notify, + SmartAllDialogType.loading, + SmartAllDialogType.toast, + }); + }); + }), + ); + super.initState(); + } + + @override + Widget build(BuildContext context) { + if (!visible) { + DialogProxy.instance.entryLoading.remove(); + return const SizedBox.shrink(); + } + + return Overlay(initialEntries: [ + if (visible) + OverlayEntry( + builder: (BuildContext context) { + if (widget.initType.contains(SmartInitType.custom)) { + DialogProxy.contextCustom = context; + } + + if (widget.initType.contains(SmartInitType.attach)) { + DialogProxy.contextAttach = context; + } + + if (widget.initType.contains(SmartInitType.notify)) { + DialogProxy.contextNotify = context; + } + + if (widget.initType.contains(SmartInitType.toast)) { + DialogProxy.contextToast = context; + } + + return const SizedBox.shrink(); + }, + ), + + // loading + if (visible && widget.initType.contains(SmartInitType.loading)) + DialogProxy.instance.entryLoading, + ]); + } + + @override + void dispose() { + widget.controller?._clearListener(); + super.dispose(); + } +} diff --git a/lib/src/widget/helper/smart_overly_controller.dart b/lib/src/widget/helper/smart_overly_controller.dart new file mode 100644 index 0000000..96bb83e --- /dev/null +++ b/lib/src/widget/helper/smart_overly_controller.dart @@ -0,0 +1,35 @@ +part of 'smart_overlay.dart'; + +/// SmartOverlay Controller +class SmartOverlayController { + VoidCallback? _onShow; + VoidCallback? _onDismiss; + + // show + Future show() async { + _onShow?.call(); + var completer = Completer(); + widgetsBinding.addPostFrameCallback((timeStamp) { + completer.complete(); + }); + await completer.future; + } + + // dismiss + void dismiss() { + _onDismiss?.call(); + } + + void _setListener({ + VoidCallback? onShow, + VoidCallback? onDismiss, + }) { + _onShow = onShow; + _onDismiss = onDismiss; + } + + void _clearListener() { + _onShow = null; + _onDismiss = null; + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 3ab4563..02401c4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: An elegant Flutter Dialog solution, Easily implement Toast, Loading and custom Dialog, Make the use of the dialog easier! -version: 4.9.6+2 +version: 4.9.7 homepage: https://github.com/fluttercandies/flutter_smart_dialog # flutter pub publish --server=https://pub.dartlang.org