introduce
Notification permissions are now confusing, so some applications have added internal notifications, which are similar to notifications but can only be displayed within the application (foreground state), essentially a view.
Next we implement a feature that displays our custom widgets as notifications.
This feature has been added to Bedrock's development framework to support single/batch notification displayCopy the code
The quick development framework of Flutter Bedrock is Mvvm+Provider
The style is as follows:
implementation
INotification
First, let’s define the notification related control behavior INotification
Abstract class INotification{// Display a notification Future showNotificationFromTop({@required Widget child,Duration animationDuration, Duration notifyDwellTime}); Future showNotifyListFromTop({@required List<Widget> children,Duration animationDuration, Duration notifyDwellTime}); Future showNotificationCustom({@required Widget Child,Duration animationDuration, Duration notifyDwellTime}); Void addNotifyListener(NotifyStatusListener) void addNotifyListener(NotifyStatusListener); void removeNotifyListener(NotifyStatusListener listener); void clearAllListener(); }Copy the code
With the behavior defined, we set out to implement it
NotificationHandler Displays a single notification
We implement the methods defined above through NotificationHandler.
Start with the showNotificationFromTop method:
Display a notificationCopy the code
@param [notifyDwellTime] @override Future showNotificationFromTop({@required Widget child,Duration animationDuration, Duration notifyDwellTime}) async{/// / by. Then, etc. Completer completer = Completer(); NotifyOverlayEntry = NotifyOverlayEntry(child, animationDuration?? Duration(milliseconds: 500), notifyDwellTime?? Duration(seconds: 2000),callback: (){ completer.complete(); // If (! streamDone){ _subscription.resume(); } /// The notification end callback _notifyListener(notifyStatus.pleted); }); /// assume [.insert] is start notify in [NotifyStatus.Running]. _notifyListener(NotifyStatus.Running); . / / / by overlay to add notification overlay of (context). Insert (notifyOverlayEntry. OverlayEntry); return completer.future; }Copy the code
To add a link between the overlay and the child (widget displayed in the notification), we define a NotifyOverlayEntry class.
NotifyOverlayEntry
class NotifyOverlayEntry{ final Widget notifyWidget; final Duration animationDuration; final Duration notifyDwellTime; final VoidCallback callback; OverlayEntry entry; bool notifyDone = false; OverlayEntry get overlayEntry => entry; NotifyOverlayEntry(this.notifyWidget, this.animationDuration, this.notifyDwellTime,{@required this.callback, NotifyType NotifyType = notifytype.fromtop}){/// Build notifications with different display modes according to the type /// currently there is only one way to slide out from the top /// If you need to expand, Switch (notifyType){case NotifyType.FromTop: entry = OverlayEntry(Builder: (ctx){ return FromTopNotifyWidget(notifyWidget,(notifyDone){ this.notifyDone = notifyDone; if(notifyDone) overlayEntry.remove(); callback(); }, animationDuration,notifyDwellTime).generateWidget(); }); break; }}}Copy the code
The code is simple enough to generate the corresponding overlayEntry based on how the notification is displayed, with the FromTopNotifyWidget responsible for the sliding out animation.
FromTopNotifyWidget
The internal implementation is very simple, just animating our child with a displacement.
Slide from (outside) the screen into the screenCopy the code
The code is as follows:
class FromTopNotifyWidget extends WidgetState with SingleTickerProviderStateMixin { final Widget child; final Duration animationDuration; final Duration notifyDwellTime; /// After the animation pops up and retracts, the callback final NotifyDone NotifyDone is executed; AnimationController controller; Animation animation; FromTopNotifyWidget(this.child,this.notifyDone, this.animationDuration, this.notifyDwellTime); @override void initState() { // TODO: implement initState super.initState(); controller = AnimationController(vsync: this,duration: animationDuration); animation = Tween<Offset>(begin: Offset(0,-1),end: Offset.zero).animate(controller); controller.addStatusListener((status) { if(status == AnimationStatus.completed){ Future.delayed(notifyDwellTime) .whenComplete(() => controller?.reverse()?.whenComplete(() => notifyDone(true))); }}); WidgetsBinding.instance.addPostFrameCallback((timeStamp) { controller.forward(); }); } @override void dispose() { controller? .dispose(); // TODO: implement dispose super.dispose(); } @override Widget build(BuildContext context) { return Column( children: [AnimatedBuilder( animation: animation, builder: (ctx,c){ return SlideTransition( position:animation , child: child,); }, )], ); }}Copy the code
At this point, the ability to display a single notification is complete, using the following method:
BuildBtn (' popup notification ', (){NotificationHandler(context).. showNotificationFromTop(notifyDwellTime: Duration(seconds: 2), child: buildNotifyChild('notification'),); }),Copy the code
After clicking the button, it looks like this:
How do you implement this when the server has multiple notifications that need to be displayed sequentially? Let’s keep going down
NotificationHandler Displays notifications in batches
Here we need to initialize a stream:
Bool streamDone = true; StreamController<NotifyListItemWrapper> _streamController; StreamSink<NotifyListItemWrapper> _sink ; StreamSubscription<NotifyListItemWrapper> _subscription; Notificationhandler. _(this.context){/// initialize in constructor _streamController = StreamController<NotifyListItemWrapper>(); _sink = _streamController.sink; / / / monitor _subscription = _streamController. Stream. Listen ((event) {if (event = = null) {/ / / when the event is empty, StreamDone = true; if(! _subscription.isPaused){ _subscription.pause(); } if(listCompleter ! = null){ listCompleter.complete(); listCompleter = null; } return ; } /// make sure notifications are given one by one _subscription. Pause (); / / / used here show a single notification method 】 showNotificationFromTop (child, the child: event. AnimationDuration: event.animationDuration,notifyDwellTime: event.notifyDwellTime); }); _streamController.done.then((v) { streamDone = true; }); _subscription.pause(); }Copy the code
To facilitate the transfer of event data, an additional NotifyListItemWrapper class is added as follows:
class NotifyListItemWrapper{
final Widget child;
final Duration animationDuration;
final Duration notifyDwellTime;
NotifyListItemWrapper(this.child, this.animationDuration, this.notifyDwellTime);
}
Copy the code
Basically a wrap around child and animation events.
Let’s see how the showNotifyListFromTop method works:
Completer listCompleter; @override Future showNotifyListFromTop({List<Widget> children, Duration animationDuration, Duration notifyDwellTime})async { listCompleter = Completer(); streamDone = false; _subscription. Resume (); Children. ForEach ((element) {/ / / batch adding data _sink. Add (NotifyListItemWrapper (element, animationDuration notifyDwellTime)); }); _sink.add(null); return listCompleter.future; }Copy the code
Now that we have implemented the batch notification display method, let’s see how to use it:
BuildBtn (' popup multiple notifications ', (){NotificationHandler(context).. showNotifyListFromTop(notifyDwellTime: Duration(seconds: 2), children:List<Widget>.generate(3, (index) => buildNotifyChild('notification $index')),). WhenComplete (() => debugPrint(' notification popup completed ')); }),Copy the code
Click the button after the style:
So far the whole function is realized, thank you for reading, if there is a mistake or better method, welcome to reply to exchange.
The Demo address
The quick development framework of Flutter Bedrock includes Demo
series
Flutter Imitation netease Cloud Music App(Basic Version)
Flutter version of imitation. Parallax effect of Zhihu list
Flutter — Realize progressive card switching for netease Cloud Music
Flutter imitates flush list of self-selected stocks