Flutter基礎(chǔ) - 深入理解Widget

1.前言

本文涵蓋了Widget经伙,StateBuildContext勿锅,InheritedWidget等術(shù)語的相關(guān)概念帕膜,并著力解答以下幾個(gè)問題:

2.Widget

在Flutter中吞鸭,幾乎任何事物都是Widget寺董。

可以把Widget想象成一種可視化組件,或者應(yīng)用中可以與可視化組件進(jìn)行交互的模塊刻剥。

Widget樹

Flutter中所有的WIdget都以樹狀結(jié)構(gòu)呈現(xiàn)遮咖。
一個(gè)包含其他Widget的Widget被稱為父Widget或者Widget容器,被包含在父Widget下的Widget被稱為子Widget造虏。
下面我們來分析示例代碼中的Widget樹結(jié)構(gòu):

code - 1.dart

其樹狀結(jié)構(gòu)如下:

widget tree

BuildContext

BuildContext是Widget樹結(jié)構(gòu)中每個(gè)Widget的上下文環(huán)境御吞,每個(gè)BuildContext都只屬于一個(gè)Widget踢械。
如果Widget A有多個(gè)子Widget,則Widget A的BuildContext是其子Widget的BuildContext的父context魄藕。
為了便于理解每個(gè)context的作用范圍,我們將前文中的Widget樹狀圖中的BuildContext用顏色進(jìn)行標(biāo)注撵术,效果如下:

widget tree with context

從BuildContext的繼承關(guān)系中背率,我們可以很容易找到Widget的父級(jí)(祖先)Widget。例如嫩与,在上圖Scaffold > Center > Column > Text這一結(jié)構(gòu)中寝姿,
通過代碼context.ancestorWidgetOfExactType(Scaffold)就可以獲取到當(dāng)前context下的第一個(gè)Scaffold。
同理划滋,也可以通過父context的關(guān)系找到對(duì)應(yīng)的子Widget饵筑,但是并不推薦這么使用,我們將在后文解釋原因处坪。

StatelessWidget

StatelessWidget一旦創(chuàng)建就無法進(jìn)行修改根资,這意味著它不會(huì)因?yàn)橥獠織l件變化而重新繪制。
一個(gè)典型的StatelessWidget示例如下:

my_stateless_widget.dart

如代碼所見同窘,StatelessWidget的生命周期非常簡單明了:

  1. 初始化玄帕;
  2. 通過build()方法進(jìn)行渲染。

StatefulWidget

與StatelessWidget相對(duì)應(yīng)的另一種Widget想邦,它可以在其生命周期中操作內(nèi)部持有數(shù)據(jù)的變化裤纹,這些數(shù)據(jù)被稱為State,這樣的Widget也叫做StatefulWidget丧没。
典型的StatefulWidget有Checkbox鹰椒,RadioSwitch等相關(guān)組件呕童,其State發(fā)生的變化將直接體現(xiàn)到UI上進(jìn)行更新漆际。
簡單的StatefulWidget示例:

my_stateful_widget.dart

我們將會(huì)在State部分詳細(xì)講解StatefulWidget的結(jié)構(gòu)與生命周期。

3.State

State作為StatefulWidget內(nèi)部數(shù)據(jù)拉庵,它的作用主要在于兩點(diǎn):

  1. 定義Widget的交互行為灿椅;
  2. 調(diào)整Widget的布局顯示。

任何State一旦發(fā)生調(diào)整都會(huì)使StatefulWidget進(jìn)行rebuild操作钞支。

BuildContext與State關(guān)系

在StatefulWidget中茫蛹,State與BuildContext唯一相關(guān),State不能修改所屬的BuildContext烁挟,而且當(dāng)Widget在樹結(jié)構(gòu)中發(fā)生位置變化時(shí)(該操作也會(huì)導(dǎo)致BuildContext的變化)婴洼,這樣的關(guān)系依然保持。
可以這樣認(rèn)為撼嗓,一旦State與BuildContext建立了關(guān)聯(lián)柬采,這種關(guān)系將一直固定存在欢唾,意味著我們不能直接通過其他BuildContext獲取到當(dāng)前context下的state

StatefulWidget生命周期

正如前文示例代碼所示粉捻,State作為StatefulWidget的主體昧诱,它可以在多個(gè)節(jié)點(diǎn)(@override所標(biāo)記的重寫方法)對(duì)State進(jìn)行調(diào)整蝌以。
當(dāng)然,還有didUpdateWidget()deactivate()畅涂,reassemble()等重寫方法并不在本文范疇中萝勤。
在下面的時(shí)序圖中我們將完整地了解StatefulWidget各個(gè)方法的調(diào)用順序(已省略部分方法)楚里,以及State與BuildContext的關(guān)聯(lián)時(shí)機(jī)旬牲,State的生效時(shí)機(jī)等:

lifecycle of stateful widget

initState()
initState()是構(gòu)造方法執(zhí)行之后第一個(gè)調(diào)用的方法,它的執(zhí)行完成標(biāo)志著state對(duì)象初始化完畢呢燥,并且在生命周期中只被調(diào)用一次崭添。
該方法重寫主要完成一些額外的初始化工作,例如animation和controller的相關(guān)初始化等叛氨,重寫時(shí)需要調(diào)用super.initState()來完成父類的初始化呼渣。
在該方法中,context對(duì)象可以訪問但是并不能拿來使用力试,因?yàn)榇藭r(shí)state與context并沒有建立關(guān)聯(lián)徙邻。

didChangeDependencies()
didChangeDependencies()是第二個(gè)調(diào)用的方法,在這一步中context可以直接使用畸裳。
該方法一般在Widget自身和InheritedWidget相關(guān)聯(lián)時(shí)或者需要?jiǎng)?chuàng)建基于context的監(jiān)聽時(shí)需要重寫缰犁,且重寫時(shí)需要調(diào)用super.didChangeDependencies()

注意怖糊,如果Widget和InheritedWidget進(jìn)行了關(guān)聯(lián)帅容,則Widget每一次進(jìn)行rebuild操作時(shí)該方法都會(huì)重復(fù)調(diào)用。

build()
build()方法在didChangeDependencies()didUpdateWidget()之后執(zhí)行伍伤,是構(gòu)建Widget及其樹形結(jié)構(gòu)的位置并徘。
該方法會(huì)在state對(duì)象發(fā)生改變或者InheritedWidget向其注冊(cè)的Widget發(fā)起通知時(shí)進(jìn)行調(diào)用,我們可以在setState((){...})方法的閉包中強(qiáng)制Widget進(jìn)行rebuild(重繪)操作扰魂。

dispose()
dispose()方法會(huì)在Widget銷毀時(shí)調(diào)用麦乞。該方法進(jìn)行一些清理操作(例如,listener劝评,controller等)姐直,注意在方法最后調(diào)用super.dispose()

如何選擇StatefulWidget與StatelessWidget

回答這個(gè)問題之前蒋畜,我們先不妨問問自己:在Widget是生命周期中声畏,是否需要一個(gè)變量來改變Widget,并且考慮如何對(duì)Widget進(jìn)行rebuild操作姻成。
如果我們回答yes插龄,那就需要StatefulWidget愿棋,否則,就應(yīng)該選擇StatelessWidget均牢。
舉個(gè)兩個(gè)栗子:

  1. 試想需要?jiǎng)?chuàng)建一個(gè)包含CheckBox的列表糠雨,列表中的每一項(xiàng)都包含了標(biāo)題和CheckBox的狀態(tài)。當(dāng)點(diǎn)擊列表中的每一項(xiàng)時(shí)徘跪,CheckBox的狀態(tài)也隨之切換见秤。在這種場景下,需要使用StatefulWidget來記錄每一項(xiàng)的狀態(tài)真椿,以及通過它才能對(duì)CheckBox進(jìn)行重繪。
  2. 在界面中有一個(gè)Form表單乎澄,表單允許用戶輸入數(shù)據(jù)并發(fā)送到服務(wù)器突硝。如果不需要在提交前做一些數(shù)據(jù)驗(yàn)證或者其他操作,StatelessWidget足夠可用置济。

StatefulWidget結(jié)構(gòu)

正如前文代碼段所示解恰,StatefulWidget包含兩個(gè)部分:

  1. 定義Widget部分;
  2. 定義State部分浙于。

定義Widget部分
該部分屬于StatefulWidget的public部分(文件外部可以通過import訪問到)护盈,在這里可以對(duì)Widget做一些初始化自定義,以及通過重寫createState()方法與私有的State對(duì)象進(jìn)行關(guān)聯(lián)羞酗。

注意腐宋,任何需要調(diào)整的變量都不應(yīng)該定義在這里,因?yàn)樵谡麄€(gè)Widget生命周期中這里的變量都不會(huì)被改變檀轨,示例代碼中parameter前的final關(guān)鍵字也說明了這點(diǎn)胸竞。

定義State部分
該部分屬于StatefulWidget的private部分(dart語言中以下劃線開頭聲明的類名,方法名参萄,變量名等都屬于作用域范圍下的私有聲明)卫枝,這里定義的變量在Widget生命周期中可以調(diào)整,并且這些調(diào)整可以應(yīng)用到Widget的重繪上讹挎。
同時(shí)校赤,_MyStatefulWidgetState內(nèi)部可以通過widget.{變量名}訪問到與之關(guān)聯(lián)的Widget中的變量,例如widget.parameter筒溃。

Widget唯一標(biāo)識(shí) - Key

在Flutter中马篮,每個(gè)Widget都有唯一標(biāo)識(shí)(Key),該標(biāo)識(shí)由系統(tǒng)框架創(chuàng)建铡羡,并且傳遞給Widget構(gòu)造方法中的可選參數(shù)*key*积蔚。
如果不顯式地傳入key,系統(tǒng)會(huì)自動(dòng)創(chuàng)建一個(gè)烦周,在一些特殊情況下尽爆,必須傳入key值怎顾,例如需要通過key來直接獲取對(duì)應(yīng)Widget的時(shí)候。
Flutter框架提供了下列key方案:

GlobalKey
該key保證整個(gè)App內(nèi)部都是唯一的募强,但是創(chuàng)建GlobalKey的代價(jià)非常昂貴,如果不需要保證整個(gè)App內(nèi)部唯一性崇摄,可以考慮使用LocalKey擎值。

LocalKey
與GlobalKey相對(duì)應(yīng)的一種局部key,需要保證創(chuàng)建LocalKey的Widget都有同一個(gè)父Widget逐抑,不然就失去其作用鸠儿。

UniqueKey
UniqueKey必須保證關(guān)聯(lián)的Widget只有一個(gè)child,屬于LocalKey厕氨。

ObjectKey
對(duì)象級(jí)別的key进每,通過Widget的實(shí)例對(duì)象來創(chuàng)建,與之類似的還有ValueKey命斧,它使用了類型來作為key值田晚,均屬于LocalKey。

訪問State

正如前文所述国葬,State與BuildContext相關(guān)聯(lián)贤徒,而BuildContext又與Widget相關(guān)聯(lián)。

Widget自身
在理論上是唯一能夠訪問state的對(duì)象汇四,而且state也可以直接訪問Widget的變/常量泞莉。

子Widget
某些時(shí)候父類Widget可能會(huì)需要訪問子類Widget的state中的值來完成一些特殊操作。
為了滿足這一需求船殉,最簡單的就是通過key來獲取鲫趁。針對(duì)以下代碼,我們可以使用myWidgetStateKey.currentState來獲取state值:

...
   GlobalKey<MyStatefulWidgetState> myWidgetStateKey = GlobalKey<MyStatefulWidgetState>();
    ...
   @override
   Widget build(BuildContext context){
      return MyStatefulWidget(
         key: myWidgetStateKey,
         color: Colors.blue,
      );
   }

注意利虫,MyStatefulWidgetState類名沒有下劃線前綴挨厚,因?yàn)槲覀冃枰獙⑵浔┞冻鰜聿趴梢栽L問到。

父Widget
試想有以下Widget的樹形結(jié)構(gòu)糠惫,底層的子Widget想訪問根節(jié)點(diǎn)Widget的state:

complex sample of widget tree

想要達(dá)成這一目標(biāo)需要滿足3個(gè)條件:

  • 根節(jié)點(diǎn)Widget需要暴露state變量疫剃,不再將state聲明為私有類型;
class MyExposingWidget extends StatefulWidget {

   MyExposingWidgetState myState;
    @override
   MyExposingWidgetState createState(){
      myState = MyExposingWidgetState();
      return myState;
   }
}
  • State必須為其中的值創(chuàng)建getter/setter或者聲明值為public(不推薦)硼讽;
class MyExposingWidgetState extends State<MyExposingWidget>{
    Color _color;

    Color get color => _color;
   ...
}
  • 底層Widget獲取到state的引用巢价。
class MyChildWidget extends StatelessWidget {


   @override
   Widget build(BuildContext context){
      final MyExposingWidget widget = context.ancestorWidgetOfExactType(MyExposingWidget);
      final MyExposingWidgetState state = widget?.myState;

      return Container(
         color: state == null ? Colors.blue : state.color,
      );
   }
}

雖然上述方案可以解決在任何地方訪問state的問題,但并不能感知state內(nèi)部的值何時(shí)進(jìn)行修改,隨之而來的Widget何時(shí)進(jìn)行重繪等問題壤躲,InheritedWidget就能幫助我們解決這一問題城菊。

4.InheritedWidget

簡而言之,InheritedWidget可以幫助我們?cè)赪idget樹形結(jié)構(gòu)中高效地傳遞數(shù)據(jù)信息碉克。作為一種特殊的Widget凌唬,它可以使Widget樹中所有的Widget都能夠共享數(shù)據(jù)。

基本概念

為了更加清楚的解釋相關(guān)概念漏麦,我們以下面代碼進(jìn)行說明:

class MyInheritedWidget extends InheritedWidget {
    MyInheritedWidget({
      Key key,
      @required Widget child,
      this.data,
   }): super(key: key, child: child);

   final data;

   static MyInheritedWidget of(BuildContext context) {
      return context.inheritFromWidgetOfExactType(MyInheritedWidget);
   }

   @override
   bool updateShouldNotify(MyInheritedWidget oldWidget) => data != oldWidget.data;
}

代碼中定義了名為MyInheritedWidget的Widget客税,它的目的即在其Widget子樹中共享其data變量。為了實(shí)現(xiàn)這一目的撕贞,我們還需要為它傳入子Widget作為構(gòu)造方法的參數(shù)更耻,才使得其子樹間的共享數(shù)據(jù)成為可能。換個(gè)更簡單的說法捏膨,如果想要某個(gè)Widget的子節(jié)點(diǎn)能共享數(shù)據(jù)酥夭,請(qǐng)使用InheritedWidget來"包裹"它。
再來看看靜態(tài)方法static MyInheritedWidget of(BuildContext context)脊奋,則實(shí)現(xiàn)了從BuildContext中獲取具體類型Widget的功能。
最后疙描,重寫updateShouldNotify方法來告知InheritedWidget的子Widget(訂閱/注冊(cè)過數(shù)據(jù)的修改通知)是否需要接收更新诚隙。
使用InheritedWidget時(shí),只需編寫類似如下代碼:

class MyParentWidget... {
       ...
      @override
      Widget build(BuildContext context){
         return MyInheritedWidget(
            data: counter,
            child: Row(
            children: <Widget>[
               ...
            ],
         ),
      );
   }
}

子Widget訪問數(shù)據(jù)

子Widget可以通過獲得InheritedWidget引用來訪問數(shù)據(jù):

class MyChildWidget... {
    ...

   @override
   Widget build(BuildContext context){
      final MyInheritedWidget inheritedWidget = MyInheritedWidget.of(context);

      ///
      /// From this moment, the widget can use the data, exposed by the MyInheritedWidget
      /// by calling: inheritedWidget.data
      ///
      return Container(
         color: inheritedWidget.data.color,
      );
   }
}

Widget交互

試想有如下WIdget樹結(jié)構(gòu):

cart sample of widget tree

為了舉例說明圖中結(jié)構(gòu)起胰,我們假設(shè)以下場景:

  • Widget A是將商品添加到購物車的按鈕久又;
  • Widget B是展示購物車中商品數(shù)量的文本;
  • Widget C是WIdget B同級(jí)的其他文本效五;
  • 我們希望按下按鈕(Widget A)時(shí) Widget B能夠準(zhǔn)確顯示購物車中的商品數(shù)量地消,而Widget C并不會(huì)發(fā)生任何重繪。

為了模擬這一需求畏妖,我們編寫以下代碼:

class Item {
   String reference;

   Item(this.reference);
}

class _MyInherited extends InheritedWidget {
   _MyInherited({
      Key key,
      @required Widget child,
      @required this.data,
   }) : super(key: key, child: child);

   final MyInheritedWidgetState data;

   @override
   bool updateShouldNotify(_MyInherited oldWidget) {
      return true;
   }
}

class MyInheritedWidget extends StatefulWidget {
   MyInheritedWidget({
      Key key,
      this.child,
   }): super(key: key);

   final Widget child;

   @override
   MyInheritedWidgetState createState() => MyInheritedWidgetState();

   static MyInheritedWidgetState of(BuildContext context){
      return (context.inheritFromWidgetOfExactType(_MyInherited) as _MyInherited).data;
   }
}

class MyInheritedWidgetState extends State<MyInheritedWidget>{
   /// List of Items
   List<Item> _items = <Item>[];

   /// Getter (number of items)
   int get itemsCount => _items.length;

   /// Helper method to add an Item
   void addItem(String reference){
      setState((){
         _items.add( Item(reference));
      });
   }

   @override
   Widget build(BuildContext context){
      return _MyInherited(
         data: this,
         child: widget.child,
      );
   }
}

class MyTree extends StatefulWidget {
   @override
   _MyTreeState createState() => _MyTreeState();
}

class _MyTreeState extends State<MyTree> {
   @override
   Widget build(BuildContext context) {
      return MyInheritedWidget(
         child: Scaffold(
         appBar: AppBar(
         title: Text('Title'),
      ),
         body: Column(
         children: <Widget>[
            WidgetA(),
            Container(
               child: Row(
               children: <Widget>[
                        Icon(Icons.shopping_cart),
                        WidgetB(),
                        WidgetC(),
                           ],
                        ),
                    ),
                ],
             ),
         ),
      );
   }
}

class WidgetA extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
      final MyInheritedWidgetState state = MyInheritedWidget.of(context);
      return Container(
         child: RaisedButton(
            child: Text('Add Item'),
            onPressed: () {
               state.addItem(' item');
            },
         ),
      );
   }
}

class WidgetB extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
      final MyInheritedWidgetState state = MyInheritedWidget.of(context);
      return Text('${state.itemsCount}');
   }
}

class WidgetC extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
      return Text('I am Widget C');
   }
}

簡要說明一下每個(gè)類的功能:

  • _MyInherited是一個(gè)InheritedWidget脉执,在點(diǎn)擊Widget A時(shí)會(huì)重復(fù)創(chuàng)建。
  • MyInheritedWidget是一個(gè)StatefulWidget戒劫,其state管理著一個(gè)商品數(shù)組半夷,通過靜態(tài)方法static MyInheritedWidgetState of(BuildContext context)來獲取state對(duì)象。
  • MyInheritedWidgetState是管理商品數(shù)組的state類迅细,同時(shí)創(chuàng)建一個(gè)itemsCount的getter方法以及addItem(String preference)方便外部調(diào)用巫橄。State中每加入一個(gè)商品,build(BuildContext context)方法會(huì)創(chuàng)建一個(gè)_MyInherited對(duì)象茵典。
  • MyTree創(chuàng)建了結(jié)構(gòu)圖中的Widget樹結(jié)構(gòu)湘换,并以MyInheritedWidget為根節(jié)點(diǎn)。
  • WidgetA是一個(gè)RaiseButton類型的Widget,點(diǎn)擊之后調(diào)用state的addItem(String preference)方法以完成商品的添加操作彩倚。
  • WidgetB是一個(gè)簡單的文本筹我,用于展示購物車中的商品數(shù)量。

那么署恍,代碼是如何實(shí)現(xiàn)Widget向State進(jìn)行注冊(cè)的呢崎溃?關(guān)鍵點(diǎn)就在于靜態(tài)方法static MyInheritedWidgetState of(BuildContext context)的內(nèi)部實(shí)現(xiàn):

static MyInheritedWidgetState of(BuildContext context){
   return (context.inheritFromWidgetOfExactType(_MyInherited) as _MyInherited).data;
}

當(dāng)子Widget調(diào)用該方法時(shí),會(huì)傳遞其BuildContext盯质,并返回對(duì)應(yīng)MyInheritedWidgetState類型的實(shí)例對(duì)象袁串。如此一來,該方法完成了兩個(gè)目的:

  • 作為數(shù)據(jù)消費(fèi)者的Widget被添加到訂閱者名單中呼巷,當(dāng)InheritedWidget管理的state發(fā)生數(shù)據(jù)變更時(shí)囱修,它會(huì)接收到通知以準(zhǔn)備重繪操作;
  • 消費(fèi)者Widget會(huì)同時(shí)獲得數(shù)據(jù)管理者state的引用王悍。

數(shù)據(jù)流向

由于WidgetA(RaiseButton)和WidgetB(文本)均通過InheritedWidget訂閱更改破镰,所以任何傳遞到_MyInherited更新的數(shù)據(jù)流向可以由以下(簡化)流程表示:

  1. 點(diǎn)擊按鈕之后,調(diào)用MyInheritedWidgetStateaddItem方法压储;
  2. addItem方法添加一個(gè)新的Item到_items中鲜漩;
  3. setState()閉包調(diào)用以準(zhǔn)備MyInheritedWidget的rebuild;
  4. 執(zhí)行build()方法后集惋,包含data(_items)的_MyInherited對(duì)象被創(chuàng)建孕似;
  5. _MyInherited通過構(gòu)造方法記錄新的state;
  6. _MyInherited設(shè)置updateShouldNotify回調(diào)為true以完成對(duì)訂閱者的通知刮刑;
  7. _MyInherited遍歷所有訂閱者(包括WidgetAWidgetB)喉祭,通知他們進(jìn)行rebuild;
  8. WidgetC不是訂閱者雷绢,因此不會(huì)rebuild泛烙。

然而,這樣一來翘紊,WidgetAWidgetB都會(huì)進(jìn)行rebuild蔽氨,但是WidgetA自身并不需要rebuild,那如何防止訪問到InheritedWidget的部分Widget不rebuild呢帆疟?
其實(shí)孵滞,之所以會(huì)出現(xiàn)這樣的情況,原因在于context.inheritFromWidgetOfExactType()方法會(huì)自動(dòng)將Widget作為訂閱鏈表上的一員鸯匹,要防止這種情況發(fā)生需修改為如下代碼:

static MyInheritedWidgetState of([BuildContext context, bool rebuild = true]) {
      return (rebuild ? context.inheritFromWidgetOfExactType(_MyInherited) as _MyInherited
                      : context.ancestorWidgetOfExactType(_MyInherited) as _MyInherited).data;
}

然后修改WidgetA如下:

class WidgetA extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
      final MyInheritedWidgetState state = MyInheritedWidget.of(context, false);
            return new Container(
                  child: new RaisedButton(
                        child: new Text('Add Item'),
                        onPressed: () {
                              state.addItem('new item');
                        },
                  ),
            );
      }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末坊饶,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子殴蓬,更是在濱河造成了極大的恐慌匿级,老刑警劉巖蟋滴,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異痘绎,居然都是意外死亡津函,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門孤页,熙熙樓的掌柜王于貴愁眉苦臉地迎上來尔苦,“玉大人,你說我怎么就攤上這事行施≡始幔” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵蛾号,是天一觀的道長稠项。 經(jīng)常有香客問我,道長鲜结,這世上最難降的妖魔是什么展运? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮精刷,結(jié)果婚禮上拗胜,老公的妹妹穿的比我還像新娘。我一直安慰自己怒允,他們只是感情好埂软,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著误算,像睡著了一般。 火紅的嫁衣襯著肌膚如雪迷殿。 梳的紋絲不亂的頭發(fā)上儿礼,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音庆寺,去河邊找鬼蚊夫。 笑死,一個(gè)胖子當(dāng)著我的面吹牛懦尝,可吹牛的內(nèi)容都是我干的知纷。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼陵霉,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼琅轧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起踊挠,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤乍桂,失蹤者是張志新(化名)和其女友劉穎冲杀,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體睹酌,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡权谁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了憋沿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片旺芽。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖辐啄,靈堂內(nèi)的尸體忽然破棺而出采章,到底是詐尸還是另有隱情,我是刑警寧澤则披,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布共缕,位于F島的核電站,受9級(jí)特大地震影響士复,放射性物質(zhì)發(fā)生泄漏图谷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一阱洪、第九天 我趴在偏房一處隱蔽的房頂上張望便贵。 院中可真熱鬧,春花似錦冗荸、人聲如沸承璃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽盔粹。三九已至,卻和暖如春程癌,著一層夾襖步出監(jiān)牢的瞬間舷嗡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國打工嵌莉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留进萄,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓锐峭,卻偏偏與公主長得像中鼠,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子沿癞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345