初識(shí)Flutter(四) Flutter的Widge

1.1 Widget 概念

我們知道在Flutter中幾乎所有的對象都是一個(gè) widget 嗦明。與原生開發(fā)中“控件”不同的是箩做,F(xiàn)lutter 中的 widget 的概念更廣泛项玛,它不僅可以表示UI元素势似,也可以表示一些功能性的組件如:用于手勢檢測的 GestureDetector 桅滋、用于APP主題數(shù)據(jù)傳遞的 Theme 等等,而原生開發(fā)中的控件通常只是指UI元素既绩。在后面的內(nèi)容中概龄,我們在描述UI元素時(shí)可能會(huì)用到“控件”、“組件”這樣的概念饲握,讀者心里需要知道他們就是 widget 私杜,只是在不同場景的不同表述而已。由于 Flutter 主要就是用于構(gòu)建用戶界面的救欧,所以衰粹,在大多數(shù)時(shí)候,可以認(rèn)為 widget 就是一個(gè)控件笆怠,不必糾結(jié)于概念铝耻。

Flutter 中是通過 Widget 嵌套 Widget 的方式來構(gòu)建UI和進(jìn)行實(shí)踐處理的,所以記住蹬刷,F(xiàn)lutter 中萬物皆為Widget瓢捉。

1.2 Flutter中的四棵樹

既然 Widget 只是描述一個(gè)UI元素的配置信息,那么真正的布局箍铭、繪制是由誰來完成的呢泊柬?Flutter 框架的的處理流程是這樣的:

  1. 根據(jù) Widget 樹生成一個(gè) Element 樹,Element 樹中的節(jié)點(diǎn)都繼承自 Element 類诈火。
  2. 根據(jù) Element 樹生成 Render 樹(渲染樹)兽赁,渲染樹中的節(jié)點(diǎn)都繼承自RenderObject 類。
  3. 根據(jù)渲染樹生成 Layer 樹冷守,然后上屏顯示刀崖,Layer 樹中的節(jié)點(diǎn)都繼承自 Layer 類。

真正的布局和渲染邏輯在 Render 樹中拍摇,Element 是 Widget 和 RenderObject 的粘合劑亮钦,可以理解為一個(gè)中間代理。我們通過一個(gè)例子來說明充活,假設(shè)有如下 Widget 樹:

Container( // 一個(gè)容器 widget
  color: Colors.blue, // 設(shè)置容器背景色
  child: Row( // 可以將子widget沿水平方向排列
    children: [
      Image.network('https://www.example.com/1.png'), // 顯示圖片的 widget
      const Text('A'),
    ],
  ),
);

注意蜂莉,如果 Container 設(shè)置了背景色蜡娶,Container 內(nèi)部會(huì)創(chuàng)建一個(gè)新的 ColoredBox 來填充背景,相關(guān)邏輯如下:

if (color != null)
  current = ColoredBox(color: color!, child: current);

而 Image 內(nèi)部會(huì)通過 RawImage 來渲染圖片映穗、Text 內(nèi)部會(huì)通過 RichText 來渲染文本窖张,所以最終的 Widget樹、Element 樹蚁滋、渲染樹結(jié)構(gòu)如下:

這里需要注意:

  1. 三棵樹中宿接,Widget 和 Element 是一一對應(yīng)的,但并不和 RenderObject 一一對應(yīng)辕录。比如 StatelessWidgetStatefulWidget 都沒有對應(yīng)的 RenderObject睦霎。
  2. 渲染樹在上屏前會(huì)生成一棵 Layer 樹。

1.3 StatelessWidget

StatelessWidget 無狀態(tài)的Widget

相對比較簡單走诞,它繼承自widget類副女,重寫了createElement()方法:

@override
StatelessElement createElement() => StatelessElement(this);

StatelessWidget用于不需要維護(hù)狀態(tài)的場景地消,它通常在build方法中通過嵌套其它 widget 來構(gòu)建UI埂息,在構(gòu)建過程中會(huì)遞歸的構(gòu)建其嵌套的 widget 。我們看一個(gè)簡單的例子

class Echo extends StatelessWidget  {
  const Echo({
    Key? key,  
    required this.text,
    this.backgroundColor = Colors.grey, //默認(rèn)為灰色
  }):super(key:key);
    
  final String text;
  final Color backgroundColor;

  @override
  widget build(BuildContext context) {
    return Center(
      child: Container(
        color: backgroundColor,
        child: Text(text),
      ),
    );
  }
}

上面的代碼梯澜,實(shí)現(xiàn)了一個(gè)回顯字符串的Echo widget 姻锁。

按照慣例,widget 的構(gòu)造函數(shù)參數(shù)應(yīng)使用命名參數(shù)猜欺,命名參數(shù)中的必需要傳的參數(shù)要添加required關(guān)鍵字位隶,這樣有利于靜態(tài)代碼分析器進(jìn)行檢查;在繼承 widget 時(shí)开皿,第一個(gè)參數(shù)通常應(yīng)該是Key涧黄。另外,如果 widget 需要接收子 widget 赋荆,那么childchildren參數(shù)通常應(yīng)被放在參數(shù)列表的最后笋妥。同樣是按照慣例, widget 的屬性應(yīng)盡可能的被聲明為final窄潭,防止被意外改變春宣。

然后我們可以通過如下方式使用它:

 Widget build(BuildContext context) {
  return Echo(text: "hello world");
}

Context

build方法有一個(gè)context參數(shù),它是BuildContext類的一個(gè)實(shí)例嫉你,表示當(dāng)前 widget 在 widget 樹中的上下文月帝,每一個(gè) widget 都會(huì)對應(yīng)一個(gè) context 對象(因?yàn)槊恳粋€(gè) widget 都是 widget 樹上的一個(gè)節(jié)點(diǎn))。實(shí)際上幽污,context是當(dāng)前 widget 在 widget 樹中位置中執(zhí)行”相關(guān)操作“的一個(gè)句柄(handle)嚷辅,比如它提供了從當(dāng)前 widget 開始向上遍歷 widget 樹以及按照 widget 類型查找父級 widget 的方法。

1.4 StatefulWidget

StatefulWidget 有狀態(tài)的Widget

StatelessWidget一樣距误,StatefulWidget也是繼承自widget類簸搞,并重寫了createElement()方法扁位,不同的是返回的Element 對象并不相同;另外StatefulWidget類中添加了一個(gè)新的接口createState()趁俊。

下面我們看看StatefulWidget的類定義:

abstract class StatefulWidget extends Widget {
  const StatefulWidget({ Key key }) : super(key: key);
    
  @override
  StatefulElement createElement() => StatefulElement(this);
    
  @protected
  State createState();
}
  • StatefulElement 間接繼承自Element類域仇,與StatefulWidget相對應(yīng)(作為其配置數(shù)據(jù))。StatefulElement中可能會(huì)多次調(diào)用createState()來創(chuàng)建狀態(tài)(State)對象则酝。
  • createState() 用于創(chuàng)建和 StatefulWidget 相關(guān)的狀態(tài)殉簸,它在StatefulWidget 的生命周期中可能會(huì)被多次調(diào)用。例如沽讹,當(dāng)一個(gè) StatefulWidget 同時(shí)插入到 widget 樹的多個(gè)位置時(shí)般卑,F(xiàn)lutter 框架就會(huì)調(diào)用該方法為每一個(gè)位置生成一個(gè)獨(dú)立的State實(shí)例,其實(shí)爽雄,本質(zhì)上就是一個(gè)StatefulElement對應(yīng)一個(gè)State實(shí)例蝠检。

1.5 State

一個(gè) StatefulWidget 類會(huì)對應(yīng)一個(gè) State 類,State表示與其對應(yīng)的 StatefulWidget 要維護(hù)的狀態(tài)挚瘟,State 中的保存的狀態(tài)信息可以:

  1. 在 widget 構(gòu)建時(shí)可以被同步讀取叹谁。
  2. 在 widget 生命周期中可以被改變,當(dāng)State被改變時(shí)乘盖,可以手動(dòng)調(diào)用其setState()方法通知Flutter 框架狀態(tài)發(fā)生改變焰檩,F(xiàn)lutter 框架在收到消息后,會(huì)重新調(diào)用其build方法重新構(gòu)建 widget 樹订框,從而達(dá)到更新UI的目的析苫。

State 中有兩個(gè)常用屬性:

  1. widget,它表示與該 State 實(shí)例關(guān)聯(lián)的 widget 實(shí)例穿扳,由Flutter 框架動(dòng)態(tài)設(shè)置衩侥。注意,這種關(guān)聯(lián)并非永久的矛物,因?yàn)樵趹?yīng)用生命周期中茫死,UI樹上的某一個(gè)節(jié)點(diǎn)的 widget 實(shí)例在重新構(gòu)建時(shí)可能會(huì)變化,但State實(shí)例只會(huì)在第一次插入到樹中時(shí)被創(chuàng)建履羞,當(dāng)在重新構(gòu)建時(shí)峦萎,如果 widget 被修改了,F(xiàn)lutter 框架會(huì)動(dòng)態(tài)設(shè)置State. widget 為新的 widget 實(shí)例吧雹。
  2. context骨杂。StatefulWidget對應(yīng)的 BuildContext,作用同StatelessWidget 的BuildContext雄卷。

State生命周期

理解State的生命周期對flutter開發(fā)非常重要搓蚪,為了加深讀者印象,本節(jié)我們通過一個(gè)實(shí)例來演示一下 State 的生命周期丁鹉。在接下來的示例中妒潭,我們?nèi)匀灰杂?jì)數(shù)器功能為例悴能,實(shí)現(xiàn)一個(gè)計(jì)數(shù)器 CounterWidget 組件 ,點(diǎn)擊它可以使計(jì)數(shù)器加1雳灾,由于要保存計(jì)數(shù)器的數(shù)值狀態(tài)漠酿,所以我們應(yīng)繼承StatefulWidget,代碼如下:

class CounterWidget extends StatefulWidget {
  const CounterWidget({Key? key, this.initValue = 0});

  final int initValue;

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

CounterWidget接收一個(gè)initValue整型參數(shù)谎亩,它表示計(jì)數(shù)器的初始值炒嘲。下面我們看一下State的代碼:

class _CounterWidgetState extends State<CounterWidget> {
  int _counter = 0;

  @override
  void initState() {
    super.initState();
    //初始化狀態(tài)  widget.initValue 獲取CounterWidget initValue;
    _counter = widget.initValue;
    print("initState");
  }

  @override
  Widget build(BuildContext context) {
    print("build");
    return Scaffold(
      body: Center(
        child: TextButton(
          child: Text('$_counter'),
          //點(diǎn)擊后計(jì)數(shù)器自增
          onPressed: () => setState(
            () => ++_counter,
          ),
        ),
      ),
    );
  }

  @override
  void didUpdateWidget(CounterWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    print("didUpdateWidget ");
  }

  @override
  void deactivate() {
    super.deactivate();
    print("deactivate");
  }

  @override
  void dispose() {
    super.dispose();
    print("dispose");
  }

  @override
  void reassemble() {
    super.reassemble();
    print("reassemble");
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print("didChangeDependencies");
  }
}

接下來,我們創(chuàng)建一個(gè)新路由匈庭,在新路由中夫凸,我們只顯示一個(gè)CounterWidget

class StateLifecycleTest extends StatelessWidget {
  const StateLifecycleTest({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return CounterWidget();
  }
}

我們運(yùn)行應(yīng)用并打開該路由頁面,在新路由頁打開后阱持,屏幕中央就會(huì)出現(xiàn)一個(gè)數(shù)字0夭拌,然后控制臺(tái)日志輸出:

I/flutter ( 5436): initState
I/flutter ( 5436): didChangeDependencies
I/flutter ( 5436): build

可以看到,在StatefulWidget插入到 widget 樹時(shí)首先initState方法會(huì)被調(diào)用衷咽。

然后我們點(diǎn)擊??按鈕熱重載鸽扁,控制臺(tái)輸出日志如下:

可以看到,在StatefulWidget插入到 widget 樹時(shí)首先initState方法會(huì)被調(diào)用镶骗。

然后我們點(diǎn)擊??按鈕熱重載桶现,控制臺(tái)輸出日志如下:

I/flutter ( 5436): reassemble
I/flutter ( 5436): didUpdateWidget 
I/flutter ( 5436): build

可以看到此時(shí)initStatedidChangeDependencies都沒有被調(diào)用,而此時(shí)didUpdateWidget被調(diào)用鼎姊。

接下來巩那,我們在 widget 樹中移除CounterWidget,將 StateLifecycleTest 的 build方法改為:

 Widget build(BuildContext context) {
  //移除計(jì)數(shù)器 
  //return CounterWidget ();
  //隨便返回一個(gè)Text()
  return Text("xxx");
}

然后熱重載此蜈,日志如下:

I/flutter ( 5436): reassemble
I/flutter ( 5436): deactive
I/flutter ( 5436): dispose

我們可以看到,在CounterWidget從 widget 樹中移除時(shí)噪生,deactivedispose會(huì)依次被調(diào)用裆赵。

StatefulWidget 生命周期如圖3-2所示:

  • initState:當(dāng) widget 第一次插入到 widget 樹時(shí)會(huì)被調(diào)用,對于每一個(gè)State對象跺嗽,F(xiàn)lutter 框架只會(huì)調(diào)用一次該回調(diào)战授,所以,通常在該回調(diào)中做一些一次性的操作桨嫁,如狀態(tài)初始化植兰、訂閱子樹的事件通知等。不能在該回調(diào)中調(diào)用BuildContext.dependOnInheritedWidgetOfExactType(該方法用于在 widget 樹上獲取離當(dāng)前 widget 最近的一個(gè)父級InheritedWidget璃吧,關(guān)于InheritedWidget我們將在后面章節(jié)介紹)楣导,原因是在初始化完成后, widget 樹中的InheritFrom widget也可能會(huì)發(fā)生變化畜挨,所以正確的做法應(yīng)該在在build()方法或didChangeDependencies()中調(diào)用它筒繁。
  • didChangeDependencies():當(dāng)State對象的依賴發(fā)生變化時(shí)會(huì)被調(diào)用噩凹;例如:在之前build() 中包含了一個(gè)InheritedWidget,然后在之后的build()Inherited widget發(fā)生了變化毡咏,那么此時(shí)Inherited widget的子 widget 的didChangeDependencies()回調(diào)都會(huì)被調(diào)用驮宴。典型的場景是當(dāng)系統(tǒng)語言 Locale 或應(yīng)用主題改變時(shí),F(xiàn)lutter 框架會(huì)通知 widget 調(diào)用此回調(diào)呕缭。
  • build():此回調(diào)讀者現(xiàn)在應(yīng)該已經(jīng)相當(dāng)熟悉了堵泽,它主要是用于構(gòu)建 widget 子樹的,會(huì)在如下場景被調(diào)用:
    1. 在調(diào)用initState()之后恢总。
    2. 在調(diào)用didUpdateWidget()之后迎罗。
    3. 在調(diào)用setState()之后。
    4. 在調(diào)用didChangeDependencies()之后离熏。
    5. 在State對象從樹中一個(gè)位置移除后(會(huì)調(diào)用deactivate)又重新插入到樹的其它位置之后佳谦。
  • reassemble():此回調(diào)是專門為了開發(fā)調(diào)試而提供的,在熱重載(hot reload)時(shí)會(huì)被調(diào)用滋戳,此回調(diào)在Release模式下永遠(yuǎn)不會(huì)被調(diào)用钻蔑。
  • didUpdateWidget ():在 widget 重新構(gòu)建時(shí),F(xiàn)lutter 框架會(huì)調(diào)用widget.canUpdate來檢測 widget 樹中同一位置的新舊節(jié)點(diǎn)奸鸯,然后決定是否需要更新咪笑,如果widget.canUpdate返回true則會(huì)調(diào)用此回調(diào)。正如之前所述娄涩,widget.canUpdate會(huì)在新舊 widget 的 keyruntimeType 同時(shí)相等時(shí)會(huì)返回true窗怒,也就是說在在新舊 widget 的key和runtimeType同時(shí)相等時(shí)didUpdateWidget()就會(huì)被調(diào)用。
  • deactivate():當(dāng) State 對象從樹中被移除時(shí)蓄拣,會(huì)調(diào)用此回調(diào)扬虚。在一些場景下,F(xiàn)lutter 框架會(huì)將 State 對象重新插到樹中球恤,如包含此 State 對象的子樹在樹的一個(gè)位置移動(dòng)到另一個(gè)位置時(shí)(可以通過GlobalKey 來實(shí)現(xiàn))辜昵。如果移除后沒有重新插入到樹中則緊接著會(huì)調(diào)用dispose()方法。
  • dispose():當(dāng) State 對象從樹中被永久移除時(shí)調(diào)用咽斧;通常在此回調(diào)中釋放資源堪置。

1.6 在 widget 樹中獲取State對象

由于 StatefulWidget 的的具體邏輯都在其 State 中,所以很多時(shí)候张惹,我們需要獲取 StatefulWidget 對應(yīng)的State 對象來調(diào)用一些方法舀锨,比如Scaffold組件對應(yīng)的狀態(tài)類ScaffoldState中就定義了打開 SnackBar(路由頁底部提示條)的方法。我們有兩種方法在子 widget 樹中獲取父級 StatefulWidget 的State 對象

通過Context獲取

context對象有一個(gè)findAncestorStateOfType()方法宛逗,該方法可以從當(dāng)前節(jié)點(diǎn)沿著 widget 樹向上查找指定類型的 StatefulWidget 對應(yīng)的 State 對象坎匿。下面是實(shí)現(xiàn)打開 SnackBar 的示例:

class GetStateObjectRoute extends StatefulWidget {
  const GetStateObjectRoute({Key? key}) : super(key: key);

  @override
  State<GetStateObjectRoute> createState() => _GetStateObjectRouteState();
}

class _GetStateObjectRouteState extends State<GetStateObjectRoute> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("子樹中獲取State對象"),
      ),
      body: Center(
        child: Column(
          children: [
            Builder(builder: (context) {
              return ElevatedButton(
                onPressed: () {
                  // 查找父級最近的Scaffold對應(yīng)的ScaffoldState對象
                  ScaffoldState _state = context.findAncestorStateOfType<ScaffoldState>()!;
                  // 打開抽屜菜單
                  _state.openDrawer();
                },
                child: Text('打開抽屜菜單1'),
              );
            }),
          ],
        ),
      ),
      drawer: Drawer(),
    );
  }
}

一般來說,如果 StatefulWidget 的狀態(tài)是私有的(不應(yīng)該向外部暴露),那么我們代碼中就不應(yīng)該去直接獲取其 State 對象碑诉;如果StatefulWidget的狀態(tài)是希望暴露出的(通常還有一些組件的操作方法)彪腔,我們則可以去直接獲取其State對象。但是通過 context.findAncestorStateOfType 獲取 StatefulWidget 的狀態(tài)的方法是通用的进栽,我們并不能在語法層面指定 StatefulWidget 的狀態(tài)是否私有德挣,所以在 Flutter 開發(fā)中便有了一個(gè)默認(rèn)的約定:如果 StatefulWidget 的狀態(tài)是希望暴露出的,應(yīng)當(dāng)在 StatefulWidget 中提供一個(gè)of 靜態(tài)方法來獲取其 State 對象快毛,開發(fā)者便可直接通過該方法來獲雀裥帷;如果 State不希望暴露唠帝,則不提供of方法屯掖。這個(gè)約定在 Flutter SDK 里隨處可見。所以襟衰,上面示例中的Scaffold也提供了一個(gè)of方法贴铜,我們其實(shí)是可以直接調(diào)用它的

比如我們想顯示 snack bar 的話可以通過下面代碼調(diào)用:

Builder(builder: (context) {
  return ElevatedButton(
    onPressed: () {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text("我是SnackBar")),
      );
    },
    child: Text('顯示SnackBar'),
  );
}),

通過GlobalKey

Flutter還有一種通用的獲取State對象的方法——通過GlobalKey來獲取瀑晒! 步驟分兩步:

  1. 給目標(biāo)StatefulWidget添加GlobalKey绍坝。

    //定義一個(gè)globalKey, 由于GlobalKey要保持全局唯一性,我們使用靜態(tài)變量存儲(chǔ)
    static GlobalKey<ScaffoldState> _globalKey= GlobalKey();
    ...
    Scaffold(
        key: _globalKey , //設(shè)置key
        ...  
    )
    
  2. 通過GlobalKey來獲取State對象

    _globalKey.currentState.openDrawer()
    

GlobalKey 是 Flutter 提供的一種在整個(gè) App 中引用 element 的機(jī)制苔悦。如果一個(gè) widget 設(shè)置了GlobalKey轩褐,那么我們便可以通過globalKey.currentWidget獲得該 widget 對象、globalKey.currentElement來獲得 widget 對應(yīng)的element對象玖详,如果當(dāng)前 widget 是StatefulWidget把介,則可以通過globalKey.currentState來獲得該 widget 對應(yīng)的state對象

注意:使用 GlobalKey 開銷較大,如果有其他可選方案蟋座,應(yīng)盡量避免使用它拗踢。另外,同一個(gè) GlobalKey 在整個(gè) widget 樹中必須是唯一的向臀,不能重復(fù)

1.7 通過 RenderObject 自定義 Widget

StatelessWidgetStatefulWidget 都是用于組合其它組件的秒拔,它們本身沒有對應(yīng)的 RenderObject。Flutter 組件庫中的很多基礎(chǔ)組件都不是通過StatelessWidgetStatefulWidget 來實(shí)現(xiàn)的飒硅,比如 Text 、Column作谚、Align等三娩,就好比搭積木,StatelessWidgetStatefulWidget 可以將積木搭成不同的樣子妹懒,但前提是得有積木雀监,而這些積木都是通過自定義 RenderObject 來實(shí)現(xiàn)的。實(shí)際上Flutter 最原始的定義組件的方式就是通過定義RenderObject 來實(shí)現(xiàn),而StatelessWidgetStatefulWidget 只是提供的兩個(gè)幫助類会前。我們簡單演示一下通過RenderObject定義組件的方式:

class CustomWidget extends LeafRenderObjectWidget{
  @override
  RenderObject createRenderObject(BuildContext context) {
    // 創(chuàng)建 RenderObject
    return RenderCustomObject();
  }
  @override
  void updateRenderObject(BuildContext context, RenderCustomObject  renderObject) {
    // 更新 RenderObject
    super.updateRenderObject(context, renderObject);
  }
}

class RenderCustomObject extends RenderBox{

  @override
  void performLayout() {
    // 實(shí)現(xiàn)布局邏輯
  }

  @override
  void paint(PaintingContext context, Offset offset) {
    // 實(shí)現(xiàn)繪制
  }
}

如果組件不會(huì)包含子組件好乐,則我們可以直接繼承自 LeafRenderObjectWidget ,它是 RenderObjectWidget 的子類瓦宜,而 RenderObjectWidget 繼承自 Widget 蔚万,我們可以看一下它的實(shí)現(xiàn):

abstract class LeafRenderObjectWidget extends RenderObjectWidget {
  const LeafRenderObjectWidget({ Key? key }) : super(key: key);

  @override
  LeafRenderObjectElement createElement() => LeafRenderObjectElement(this);
}

很簡單,就是幫 widget 實(shí)現(xiàn)了createElement 方法临庇,它會(huì)為組件創(chuàng)建一個(gè) 類型為 LeafRenderObjectElement 的 Element對象反璃。如果自定義的 widget 可以包含子組件,則可以根據(jù)子組件的數(shù)量來選擇繼承SingleChildRenderObjectWidget 或 MultiChildRenderObjectWidget假夺,它們也實(shí)現(xiàn)了createElement() 方法淮蜈,返回不同類型的 Element 對象。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末已卷,一起剝皮案震驚了整個(gè)濱河市梧田,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌侧蘸,老刑警劉巖裁眯,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異闺魏,居然都是意外死亡未状,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門析桥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來司草,“玉大人,你說我怎么就攤上這事泡仗÷窈纾” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵娩怎,是天一觀的道長搔课。 經(jīng)常有香客問我,道長截亦,這世上最難降的妖魔是什么爬泥? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮崩瓤,結(jié)果婚禮上袍啡,老公的妹妹穿的比我還像新娘。我一直安慰自己却桶,他們只是感情好境输,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布蔗牡。 她就那樣靜靜地躺著,像睡著了一般嗅剖。 火紅的嫁衣襯著肌膚如雪辩越。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天信粮,我揣著相機(jī)與錄音黔攒,去河邊找鬼。 笑死蒋院,一個(gè)胖子當(dāng)著我的面吹牛亏钩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播欺旧,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼姑丑,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了辞友?” 一聲冷哼從身側(cè)響起栅哀,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎称龙,沒想到半個(gè)月后留拾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鲫尊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年痴柔,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片疫向。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡咳蔚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出搔驼,到底是詐尸還是另有隱情谈火,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布舌涨,位于F島的核電站糯耍,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏囊嘉。R本人自食惡果不足惜温技,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望扭粱。 院中可真熱鬧舵鳞,春花似錦、人聲如沸焊刹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽虐块。三九已至俩滥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間贺奠,已是汗流浹背霜旧。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留儡率,地道東北人挂据。 一個(gè)月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像儿普,于是被迫代替她去往敵國和親崎逃。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容