Flutter 如何創(chuàng)建Toast或Notifications?疊加的概念

如何在Flutter的任何屏幕/頁(yè)面上顯示內(nèi)容窃判?

難度:中級(jí)

前言

最近我寫(xiě)了一些代碼來(lái)處理WebSockets,我需要在任何屏幕/頁(yè)面的頂部顯示一個(gè)圖標(biāo)喇闸,以便在服務(wù)器發(fā)送通知時(shí)通知用戶袄琳。

我嘗試使用PopupRouteshowDialog ...但是永遠(yuǎn)無(wú)法獲得我想要實(shí)現(xiàn)的目標(biāo)燃乍。

當(dāng)我們使用路由時(shí)唆樊,整個(gè)屏幕被覆蓋,并且用戶無(wú)法繼續(xù)使用當(dāng)前頁(yè)面“ 正常 ” 工作刻蟹,因?yàn)楹笳弑涣硪粋€(gè)頁(yè)面替換(覆蓋)逗旁。因此,我繼續(xù)我的調(diào)查舆瘪,發(fā)現(xiàn)OverlayOverlayEntry的概念片效。

通過(guò)閱讀Flutter的源代碼,我發(fā)現(xiàn)Flutter使用OverlayEntry來(lái)顯示drag avatar(請(qǐng)參閱Draggable)英古。因此淀衣,我明白這就是我要找的東西。

覆蓋

Flutter文檔說(shuō):“ * overlay是一組可以獨(dú)立管理的條目召调。Overlays讓獨(dú)立的widgets“浮動(dòng)”在其他widgets之上......* ”膨桥。

用我自己很簡(jiǎn)單的話說(shuō)。

overlay不外乎是一個(gè)layer (StatefulWidget)唠叛,在所有widget之上只嚣,它包含一個(gè)Stack,這里我們可以添加任何小部件艺沼。

這些小部件稱為OverlayEntry

OverlayState

Overlay是一個(gè)StatefulWidget册舞。OverlayState是Overlay實(shí)例的State,負(fù)責(zé)渲染澳厢。

OverlayEntry

OverlayEntry是一個(gè)Widget环础,我們可以把他插入到OverlayOverlayEntry一次最多只能插入到一個(gè)Overlay剩拢。

由于Overlay使用Stack布局线得,因此overlay窗口可以使用PositionedAnimatedPositioned將自己定位在疊加層中。

嗯徐伐,這正是我需要的:能夠在屏幕上的任何地方顯示我的通知圖標(biāo)贯钩。

讓我們直接跳轉(zhuǎn)到一些代碼

以下類在坐標(biāo)(50.0,50.0)處顯示一個(gè)Icon,并在2秒后將其刪除办素。

import 'package:flutter/material.dart';
import 'dart:async';

class ShowNotificationIcon {

    void show(BuildContext context) async {
        OverlayState overlayState = Overlay.of(context);
        OverlayEntry overlayEntry = new OverlayEntry(builder: _build);

        overlayState.insert(overlayEntry);

        await new Future.delayed(const Duration(seconds: 2));

        overlayEntry.remove();
    }

    Widget _build(BuildContext context){
      return new Positioned(
        top: 50.0,
        left: 50.0,
        child: new Material(
            color: Colors.transparent,
            child: new Icon(Icons.warning, color: Colors.purple),
        ),
      );
    }
}

如您所見(jiàn)角雷,為了能夠顯示圖標(biāo),我們需要提供Context性穿。為什么勺三?

官方文檔沒(méi)有解釋這一點(diǎn),但是需曾,看一下源代碼吗坚,我們看到為每個(gè)Route創(chuàng)建了一個(gè)Overlay,因此它屬于一個(gè)Context樹(shù)(參見(jiàn)我關(guān)于上下文概念的文章)呆万。因此商源,有必要找到對(duì)應(yīng)于特定ContextOverlayState第7行)。

就是這個(gè)谋减! 從這個(gè)例子中牡彻,我們可以推導(dǎo)出任何類型的疊加內(nèi)容和行為。

為了說(shuō)明這一點(diǎn)出爹,讓我們構(gòu)建一種閃爍的Toast庄吼,它在屏幕上的某個(gè)位置顯示一個(gè)Widget,并在一定時(shí)間后消失以政。

示例:閃爍Toast

第一類是前一個(gè)例子的概括霸褒。它允許提供外部Widget構(gòu)造函數(shù)。

import 'dart:async';
import 'package:flutter/material.dart';

class BlinkingToast {
    bool _isVisible = false;

    ///
    /// BuildContext context: the context from which we need to retrieve the Overlay
    /// WidgetBuilder externalBuilder: (compulsory) external routine that builds the Widget to be displayed
    /// Duration duration: (optional) duration after which the Widget will be removed
    /// Offset position: (optional) position where you want to show the Widget
    ///
    void show({
        @required BuildContext context,
        @required WidgetBuilder externalBuilder, 
        Duration duration = const Duration(seconds: 2),
        Offset position = Offset.zero,
        }) async {

        // Prevent from showing multiple Widgets at the same time
        if (_isVisible){
            return;
        }

        _isVisible = true;

        OverlayState overlayState = Overlay.of(context);
        OverlayEntry overlayEntry = new OverlayEntry(
            builder: (BuildContext context) => new BlinkingToastWidget(
                widget: externalBuilder(context),
                position: position,
            ),
        );
        overlayState.insert(overlayEntry);

        await new Future.delayed(duration);

        overlayEntry.remove();

        _isVisible = false;
    }
}

第二個(gè)類在屏幕上的某個(gè)位置顯示W(wǎng)idget并使其閃爍盈蛮。

有關(guān)動(dòng)畫(huà)概念的進(jìn)一步說(shuō)明废菱,請(qǐng)參閱我關(guān)于此主題的文章

class BlinkingToastWidget extends StatefulWidget {
    BlinkingToastWidget({
        Key key,
        @required this.widget,
        @required this.position,
    }): super(key: key);

    final Widget widget;
    final Offset position;

    @override
    _BlinkingToastWidgetState createState() => new _BlinkingToastWidgetState();
}

class _BlinkingToastWidgetState extends State<BlinkingToastWidget>
    with SingleTickerProviderStateMixin {

AnimationController _controller;
  Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    _controller = new AnimationController(
        duration: const Duration(milliseconds: 500), vsync: this);
    _animation = new Tween(begin: 0.0, end: 1.0).animate(new CurvedAnimation(
      parent: _controller,
      curve: new Interval(0.0, 0.5)
    ))
      ..addListener(() {
        if (mounted){
          setState(() {
            // Refresh
          });
        }
      })
      ..addStatusListener((AnimationStatus status){
        if (status == AnimationStatus.completed){
          _controller.reverse().orCancel;
        } else if (status == AnimationStatus.dismissed){
          _controller.forward().orCancel;
        }
      });
    _controller.forward().orCancel;
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return new Positioned(
        top:  widget.position.dy,
        left: widget.position.dx,
        child: new IgnorePointer(
          child: new Material(
            color: Colors.transparent,
            child: new Opacity(
              opacity: _animation.value,
              child: widget.widget,
            ),
          ),
        ));
  }
}

要調(diào)用此BlinkingToast:

BlinkingToast toast = new BlinkingToast();

toast.show(
    context: context,
    externalBuilder: (BuildContext context){
        return new Icon(Icons.warning, color: Colors.purple);
    },
    duration: new Duration(seconds: 5),
    position: new Offset(50.0, 50.0),
);

結(jié)論

這是一篇非常簡(jiǎn)短的文章抖誉,僅旨在分享在任何屏幕上顯示W(wǎng)idget的方式殊轴。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市袒炉,隨后出現(xiàn)的幾起案子旁理,更是在濱河造成了極大的恐慌,老刑警劉巖我磁,帶你破解...
    沈念sama閱讀 217,826評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件孽文,死亡現(xiàn)場(chǎng)離奇詭異驻襟,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)芋哭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)沉衣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人减牺,你說(shuō)我怎么就攤上這事豌习。” “怎么了拔疚?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,234評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵肥隆,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我稚失,道長(zhǎng)栋艳,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,562評(píng)論 1 293
  • 正文 為了忘掉前任墩虹,我火速辦了婚禮嘱巾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘诫钓。我一直安慰自己旬昭,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布菌湃。 她就那樣靜靜地躺著问拘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪惧所。 梳的紋絲不亂的頭發(fā)上骤坐,一...
    開(kāi)封第一講書(shū)人閱讀 51,482評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音下愈,去河邊找鬼纽绍。 笑死,一個(gè)胖子當(dāng)著我的面吹牛势似,可吹牛的內(nèi)容都是我干的拌夏。 我是一名探鬼主播,決...
    沈念sama閱讀 40,271評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼履因,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼障簿!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起栅迄,我...
    開(kāi)封第一講書(shū)人閱讀 39,166評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤站故,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后毅舆,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體西篓,經(jīng)...
    沈念sama閱讀 45,608評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡愈腾,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了岂津。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片顶滩。...
    茶點(diǎn)故事閱讀 39,926評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖寸爆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情盐欺,我是刑警寧澤赁豆,帶...
    沈念sama閱讀 35,644評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站冗美,受9級(jí)特大地震影響魔种,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜粉洼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評(píng)論 3 329
  • 文/蒙蒙 一节预、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧属韧,春花似錦安拟、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,866評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至锅棕,卻和暖如春拙泽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背裸燎。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,991評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工顾瞻, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人德绿。 一個(gè)月前我還...
    沈念sama閱讀 48,063評(píng)論 3 370
  • 正文 我出身青樓荷荤,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親脆炎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子梅猿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評(píng)論 2 354