Day05 - Flutter開發初體驗

一、創建Flutter項目
創建Flutter項目有兩種方式:通過命令行創建 和 通過開發工具創建

  • 1.1、在終端通過命令行創建(全新的APP)

    flutter create 項目的名字
    

    提示:

    • Flutter的名稱不要包含特殊的字符,另外不可以使用駝峰標識(不要使用大寫),名字很長可以使用下劃線連接
    • 創建完之后使用自己喜歡的開發工具打開即可,比如:Android Sdudio、VSCode

    創建擴展: 語言的設置

    • flutter create -i swift -a kotlin 項目的名字
    • flutter create -i swift -a java 項目的名字
    • flutter create -i objc -a kottlin 項目的名字
    • flutter create -i objc -a java 項目的名字

    2>、混合項目的創建(混編到已有的Android / iOS工程內)

    • flutter create --template module 組件的名字

    3>、Flutter Plugin的創建(Flutter平臺插件工程,包含Dart層與Native平臺層的實現)

    • flutter create --template=plugin xxapp_plugin

    4>、Flutter Package(Flutter純Dart插件工程,僅包含Dart層的實現,往往定義一些公共Widget)

    • flutter create --template=package xxapp_package
  • 1.2、目錄結構概述


    flutter 目錄結構概述
    • dart_tool:記錄一些東西所在的位置以及它的一些版本信息,不需要我們去配置,不要手動去修改
    • idea: 記錄當前項目的一些配置的
    • iml:也是對記錄對當前項目的配置
    • Android:安卓的代碼
    • build:項目的構建輸出目錄
    • iOS:iOS的代碼
    • lib:flutter 源碼的文件夾
    • test:主要是用來做一些測試的
    • gitignore:記錄當前的一些忽略的
    • metadate:對flutter版本的一些記錄,不需要手動去改
    • packages、pubspec.lock 都是導入到三方后生成的
    • pubspec.yaml:項目依賴配置文件類似于RN的 package.json
  • 1.3、Flutter Hot Reload 熱重載Flutter Hot Restart 熱重啟 的區別

    Flutter Hot Reload 熱重載 和 Flutter Hot Restart 熱重啟

    • Flutter Hot Reload 熱重載:主要是執行 build 方法
    • Flutter Hot Restart 熱重啟:重新運行整個app(熱啟動)

    提示:冷啟動是從未啟動過app

二、Flutter代碼展示,看不懂沒關系,這篇博客僅僅是展示,下篇才開始學習

  • 2.1、Hello World的實現
    我們在 main.dart 里面進行從 0 到 1 編寫代碼

    import 'package:flutter/material.dart';
    
    main(List<String> args) {
         runApp(Text("Hello World", textDirection: TextDirection.ltr));
    }
    

    當然,上面的代碼我們已經實現了在界面上顯示Hello World:
    但是沒有居中,字體也有點小;
    這些問題,我們放到后面再來解決,先搞懂目前的幾行代碼;
    上面的代碼我們有一些比較熟悉,有一些并不清楚是什么:
    比如我們知道Dart程序的入口都是main函數,而Flutter是Dart編寫的,所以入口也是main函數;
    但是我們導入的Material是什么呢?
    另外,我們在main函數中調用了一個runApp()函數又是什么呢?
    下面,我們對不認識的代碼進行一些分析。

  • 2.2、代碼分析

    • 1>、runApp 和 Widget
      runApp是Flutter內部提供的一個函數,當我們啟動一個Flutter應用程序時就是從調用這個函數開始的

      • 我們可以點到runApp的源碼,查看到該函數

      • 我們暫時不分析具體的源碼(因為我發現過多的理論,對于初學者來說并不友好)

        void runApp(Widget app) {
            ...省略代碼
        }
        

        該函數讓我們傳入一個東西:Widget?
        我們先說Widget的翻譯:

        • Widget在國內有很多的翻譯;
          • 做過Android、iOS等開發的人群,喜歡將它翻譯成控件;
          • 做過Vue、React等開發的人群,喜歡將它翻譯成組件;
          • 如果我們使用Google,Widget翻譯過來應該是小部件;
          • 沒有說哪種翻譯一定是對的,或者一定是錯的,但是我個人更傾向于小部件或者組件;
        • Widget到底什么東西呢?
          • 我們學習Flutter,從一開始就可以有一個基本的認識:Flutter中萬物皆Widget(萬物皆可盤);
          • 在我們iOS或者Android開發中,我們的界面有很多種類的劃分:應用(Application)、視圖控制器(View Controller)、活動(Activity)、View(視圖)、Button(按鈕)等等;
          • 但是在Flutter中,這些東西都是不同的Widget而已;
          • 也就是我們整個應用程序中所看到的內容幾乎都是Widget,甚至是內邊距的設置,我們也需要使用一個叫Padding的Widget來做;
        • runApp函數讓我們傳入的就是一個Widget:
          • 但是我們現在沒有Widget,怎么辦呢?
          • 我們可以導入Flutter默認已經給我們提供的Material庫,來使用其中的很多內置Widget;
    • 2>、Material 設計風格

      • material是什么呢?

        • material是Google公司推行的一套設計風格,或者叫設計語言、設計規范等;
        • 里面有非常多的設計規范,比如顏色、文字的排版、響應動畫與過度、填充等等;
        • 在Flutter中高度集成了Material風格的Widget;
        • 在我們的應用中,我們可以直接使用這些Widget來創建我們的應用(后面會用到很多);
      • Text小部件分析:

        • 我們可以使用Text小部件來完成文字的顯示;

        • 我們發現Text小部件繼承自StatelessWidget,StatelessWidget繼承自Widget;

        • 所以我們可以將Text小部件傳入到runApp函數中

        • 屬性非常多,但是我們已經學習了Dart語法,所以你會發現只有this.data屬性是必須傳入的。

          class Text extends StatelessWidget {
              const Text(
              this.data, {
              Key key,
              this.style,
              this.strutStyle,
              this.textAlign,
              this.textDirection,
              this.locale,
              this.softWrap,
              this.overflow,
              this.textScaleFactor,
              this.maxLines,
              this.semanticsLabel,
              this.textWidthBasis,
              });
          }
          
      • StatelessWidget簡單介紹:

        • StatelessWidget繼承自Widget;

        • 后面我會更加詳細的介紹它的用法;

          abstract class StatelessWidget extends Widget {
             // ...省略代碼
          }
          
  • 2.3、代碼改進

    • 1>、改進界面樣式
      我們可能希望文字居中顯示,并且可以大一些;
      居中顯示: 需要使用另外一個Widget,Center;
      文字大一些: 需要給Text文本設置一些樣式;
      我們修改代碼如下:
      我們在Text小部件外層包裝了一個Center部件,讓Text作為其child;
      并且,我們給Text組件設置了一個屬性:style,對應的值是TextStyle類型;

      import 'package:flutter/material.dart';
      
      main(List<String> args) {
          runApp(Center(
              child: Text(
                 "Hello World",
                 textDirection: TextDirection.ltr,
                 style: TextStyle(fontSize: 20),
              ),
           )
         );
      }
      
    • 2>、改進界面結構
      目前我們雖然可以顯示HelloWorld,但是我們發現最底部的背景是黑色,并且我們的頁面并不夠結構化。
      正常的App頁面應該有一定的結構,比如通常都會有導航欄,會有一些背景顏色
      在開發當中,我們并不需要從零去搭建這種結構化的界面,我們可以使用Material庫,直接使用其中的一些封裝好的組件來完成一些結構的搭建。
      我們通過下面的代碼來實現:

      import 'package:flutter/material.dart';
      
      main(List<String> args) {
          runApp(
             MaterialApp(
                home: Scaffold(
                   appBar: AppBar(
                       title: Text("CODERWHY"),
                   ),
                   body: Center(
                       child: Text(
                       "Hello World",
                       textDirection: TextDirection.ltr,
                       style: TextStyle(fontSize: 36),
                   ),
                ),
             ),
          )
        );
      }
      
      • 在最外層包裹一個MaterialApp
        • 這意味著整個應用我們都會采用MaterialApp風格的一些東西,方便我們對應用的設計,并且目前我們使用了其中兩個屬性;
        • title:這個是定義在Android系統中打開多任務切換窗口時顯示的標題;(暫時可以不寫)
        • home:是該應用啟動時顯示的頁面,我們傳入了一個Scaffold;
      • Scaffold是什么呢?
        • 翻譯過來是腳手架,腳手架的作用就是搭建頁面的基本結構;
        • 所以我們給MaterialApp的home屬性傳入了一個Scaffold對象,作為啟動顯示的Widget;
        • Scaffold也有一些屬性,比如appBar和body;
        • appBar是用于設計導航欄的,我們傳入了一個title屬性;
        • body是頁面的內容部分,我們傳入了之前已經創建好的Center中包裹的一個Text的Widget;
  • 2.4、代碼重構

    • 1>、創建自己的Widget
      很多學習Flutter的人,都會被Flutter的嵌套勸退,當代碼嵌套過多時,結構很容易看不清晰。

      • 這里有兩點我先說明一下:
        • 1、Flutter整個開發過程中就是形成一個Widget樹,所以形成嵌套是很正常的。
        • 2、關于Flutter的代碼縮進,更多開發中我們使用的是2個空格(前端開發2個空格居多,你喜歡4個也沒問題)
      • 但是,我們開發一個這么簡單的程序就出現如此多的嵌套,如果應用程序更復雜呢?
        • 我們可以對我們的代碼進行封裝,將它們封裝到自己的Widget中,創建自己的Widget;
      • 如何創建自己的Widget呢?
        • 在Flutter開發中,我們可以繼承自StatelessWidget或者StatefulWidget來創建自己的Widget類;
        • StatelessWidget: 沒有狀態改變的Widget,通常這種Widget僅僅是做一些展示工作而已;
        • StatefulWidget: 需要保存狀態,并且可能出現狀態改變的Widget;

      在上面的案例中對代碼的重構,我們使用StatelessWidget即可,所以我們接下來學習一下如果利用StatelessWidget來對我們的代碼進行重構;
      StatefulWidget我們放到后面的一個案例中來學習;

    • 2>、StatelessWidget

      • StatelessWidget通常是一些沒有狀態(State,也可以理解成data)需要維護的Widget:
        • 它們的數據通常是直接寫死(放在Widget中的數據,必須被定義為final,為什么呢?我在下一個章節講解StatefulWidget會講到);
        • 從parent widget中傳入的而且一旦傳入就不可以修改;
        • 從InheritedWidget獲取來使用的數據(這個放到后面會講解);
      • 我們來看一下創建一個StatelessWidget的格式:
        • 讓自己創建的Widget繼承自StatelessWidget;

        • StatelessWidget包含一個必須重寫的方法:build方法;

          class MyStatelessWidget extends StatelessWidget {
             @override
             Widget build(BuildContext context) {
                 return <返回我們的Widget要渲染的Widget,比如一個Text Widget>;
             }
          }
          
      • build方法的解析:
        • Flutter在拿到我們自己創建的StatelessWidget時,就會執行它的build方法;
        • 我們需要在build方法中告訴Flutter,我們的Widget希望渲染什么元素,比如一個Text Widget;
        • StatelessWidget沒辦法主動去執行build方法,當我們使用的數據發生改變時,build方法會被重新執行;
      • build方法什么情況下被執行呢 ?
        • 當我們的StatelessWidget第一次被插入到Widget樹中時(也就是第一次被創建時);
        • 當我們的父Widget(parent widget)發生改變時,子Widget會被重新構建;
        • 如果我們的Widget依賴InheritedWidget的一些數據,InheritedWidget數據發生改變時;
    • 3>、重構案例代碼



      現在我們就可以通過StatelessWidget來對我們的代碼進行重構了

      • 因為我們的整個代碼都是一些數據展示,沒有數據的改變,使用StatelessWidget即可;
      • 另外,為了體現更好的封裝性,我對代碼進行了兩層的拆分,讓代碼結構看起來更加清晰;(具體的拆分方式,我會在后面的案例中不斷的體現出來,目前我們先拆分兩層)

      重構后的代碼如下:

      import 'package:flutter/material.dart';
      
      main(List<String> args) {
          runApp(MyApp());
      }
      
      class MyApp extends StatelessWidget {
        @override
        Widget build(BuildContext context) {
            return MaterialApp(
                home: Scaffold(
                  appBar: AppBar(
                  title: Text("CODERWHY"),
                ),
                body: HomeContent(),
            ),
          )
        }
      }
      
      class HomeContent extends StatelessWidget {
         @override
         Widget build(BuildContext context) {
            return Center(
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                      Checkbox(
                         value: true,
                         onChanged: (value) => print("Hello World")
                   ),
                   Text(
                      "同意協議",
                      textDirection: TextDirection.ltr,
                      style: TextStyle(fontSize: 20),
                   )
                 ],
               ),
            );
         }
      }
      
  • 2.5、StateFulWidget (有狀態的)
    在開發中,某些Widget情況下我們展示的數據并不是一層不變的:
    比如Flutter默認程序中的計數器案例,點擊了+號按鈕后,顯示的數字需要+1;
    比如在開發中,我們會進行下拉刷新、上拉加載更多,這時數據也會發生變化;
    而StatelessWidget通常用來展示哪些數據固定不變的,如果數據會發生改變,我們使用StatefulWidget;

    • 1>、StatefulWidget介紹
      為什么選擇StatefulWidget呢?
      在示例代碼中,當我們點擊按鈕時,界面上顯示的數據會發生改變;
      這時,我們需要一個變量來記錄當前的狀態,再把這個變量顯示到某個Text Widget上;
      并且每次變量發生改變時,我們對應的Text上顯示的內容也要發生改變;
      但是有一個問題,我之前說過定義到Widget中的數據都是不可變的,必須定義為final,為什么呢?
      這次因為Flutter在設計的時候就決定了一旦Widget中展示的數據發生變化,就重新構建整個Widget;Flutter通過一些機制來限定定義到Widget中的成員變量必須是final的;

      • Flutter如何做到我們在開發中定義到Widget中的數據一定是final的呢?,我們來看一下Widget的源碼

        @immutable
        abstract class Widget extends DiagnosticableTree {
             // ...省略代碼
        }
        

        @immutable 注解標明的類或者子類都必須是不可變的

      提示:定義到Widget中的數據一定是不可變的,需要使用final來修飾

    • 2>、如何存儲Widget狀態?

      • 既然Widget是不可變,那么StatefulWidget如何來存儲可變的狀態呢?
        • StatelessWidget無所謂,因為它里面的數據通常是直接定義完后就不修改的。
        • 但StatefulWidget需要有狀態(可以理解成變量)的改變,這如何做到呢?
      • Flutter將StatefulWidget設計成了兩個類:
        • 也就是你創建StatefulWidget時必須創建兩個類:
        • 一個類繼承自StatefulWidget,作為Widget樹的一部分;
        • 一個類繼承自State,用于記錄StatefulWidget會變化的狀態,并且根據狀態的變化,構建出新的Widget;
      • 創建一個StatefulWidget,我們通常會按照如下格式來做:
        • 當Flutter在構建Widget Tree時,會獲取State的實例,并且它調用build方法去獲取StatefulWidget希望構建的Widget;

        • 那么,我們就可以將需要保存的狀態保存在MyState中,因為它是可變的;

          class MyStatefulWidget extends StatefulWidget {
               @override
               State<StatefulWidget> createState() {
                   // 將創建的State返回
                   return MyState();
               }
          }
          
          class MyState extends State<MyStatefulWidget> {
               @override
               Widget build(BuildContext context) {
                   return <構建自己的Widget>;
               }
          }
          

        提示:Flutter這樣設計的目的是:在Flutter中,只要數據改變了Widget就需要重新構建(rebuild)

  • 2.6、StatefulWidget案例
    我們通過一個案例來練習一下StatefulWidget,還是之前的計數器案例,但是我們按照自己的方式進行一些改進。
    案例效果以及布局如下:

    • 在這個案例中,有很多布局對于我們來說有些復雜,我們后面會詳細學習,建議大家根據我的代碼一步步寫出來來熟悉Flutter開發模式;

    • Column小部件:之前我們已經用過,當有垂直方向布局時,我們就使用它;

    • Row小部件:之前也用過,當時水平方向布局時,我們就使用它;

    • RaiseButton小部件:可以創建一個按鈕,并且其中有一個onPress屬性是傳入一個回調函數,當按鈕點擊時被回調;


    • 1>、 創建StatefulWidget,下面我們來看看代碼實現:

      • 為當點擊按鈕時,數字會發生變化,所以我們需要使用一個StatefulWidget,所以我們需要創建兩個類;

      • MyCounterWidget繼承自StatefulWidget,里面需要實現createState方法;

      • MyCounterState繼承自State,里面實現build方法,并且可以定義一些成員變量

        class MyCounterWidget extends StatefulWidget {
          @override
          State<StatefulWidget> createState() {
               // 將創建的State返回
               return MyCounterState();
          }
        }
        
        class MyCounterState extends State<MyCounterWidget> {
            int counter = 0;
        
           @override
           Widget build(BuildContext context) {
               return Center(
                   child: Text("當前計數:$counter", style: TextStyle(fontSize: 30),),
               );
           }
        }
        
    • 2>、實現按鈕的布局

      class MyCounterState extends State<MyCounterWidget> {
          int counter = 0;
      
          @override
          Widget build(BuildContext context) {
             return Center(
                 child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                        Row(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: <Widget>[
                              RaisedButton(
                                 color: Colors.redAccent,
                                 child: Text("+1", style: TextStyle(fontSize: 18, color: Colors.white),),
                                 onPressed: () {
      
                                 },
                              ),
                              RaisedButton(
                                 color: Colors.orangeAccent,
                                 child: Text("-1", style: TextStyle(fontSize: 18, color: Colors.white),),
                                 onPressed: () {
      
                                 },
                              )
                          ],
                        ),
                       Text("當前計數:$counter", style: TextStyle(fontSize: 30),)
                      ],
                 ),
             );
         }
      }
      
    • 3>、按鈕點擊狀態改變

      • 我們現在要監聽狀態的改變,當狀態改變時要修改counter變量:
        • 但是,直接修改變量可以改變界面嗎?不可以。
        • 這是因為Flutter并不知道我們的數據發生了改變,需要來重新構建我們界面中的Widget;
      • 如何可以讓Flutter知道我們的狀態發生改變了,重新構建我們的Widget呢?
        • 我們需要調用一個State中默認給我們提供的setState方法;

        • 可以在其中的回調函數中修改我們的變量;

          onPressed: () {
             setState(() {  
                counter++;  
             });   
          },   
          
  • 2.7、StatefulWidget 生命周期

    • 1>、生命周期的理解

      • 什么是生命周期呢?
        • 客戶端開發:iOS開發中我們需要知道UIViewController從創建到銷毀的整個過程,Android開發中我們需要知道Activity從創建到銷毀的整個過程。以便在不同的生命周期方法中完成不同的操作;
        • 前端開發中:Vue、React開發中組件也都有自己的生命周期,在不同的生命周期中我們可以做不同的操作;
      • Flutter小部件的生命周期:
        • StatelessWidget可以由父Widget直接傳入值,調用build方法來構建,整個過程非常簡單;
        • 而StatefulWidget需要通過State來管理其數據,并且還要監控狀態的改變決定是否重新build整個Widget;
        • 所以,我們主要討論StatefulWidget的生命周期,也就是它從創建到銷毀的整個過程;
    • 2>、生命周期
      那么StatefulWidget有哪些生命周期的回調呢?它們分別在什么情況下執行呢?
      atefulWidget本身由兩個類組成的:StatefulWidget和State,我們分開進行分析
      首先,執行StatefulWidget中相關的方法:

      • 1、執行StatefulWidget的構造函數(Constructor)來創建出StatefulWidget;
      • 2、執行StatefulWidget的createState方法,來創建一個維護StatefulWidget的State對象;

      其次,調用createState創建State對象時,執行State類的相關方法:

      • 1、執行State類的構造方法(Constructor)來創建State對象;
      • 2、執行initState,我們通常會在這個方法中執行一些數據初始化的操作,或者也可能會發送網絡請求;
      • 3、執行didChangeDependencies方法,這個方法在兩種情況下會調用
        • 情況一:調用initState會調用;
        • 情況二:從其他對象中依賴一些數據發生改變時,比如前面我們提到的InheritedWidget;
      • 4、Flutter執行build方法,來看一下我們當前的Widget需要渲染哪些Widget;
      • 5、當前的Widget不再使用時,會調用dispose進行銷毀;
      • 6、手動調用setState方法,會根據最新的狀態(數據)來重新調用build方法,構建對應的Widgets;
      • 7、執行didUpdateWidget方法是在當父Widget觸發重建(rebuild)時,系統會調用didUpdateWidget方法;

      過代碼進行演示:

      import 'package:flutter/material.dart';
      
      main(List<String> args) {
          runApp(MyApp());
      }
      
      class MyApp extends StatelessWidget {
          @override
          Widget build(BuildContext context) {
               return MaterialApp(
                   home: Scaffold(
                     appBar: AppBar(
                         title: Text("HelloWorld"),
                     ),
                   body: HomeBody(),
                ),
           );
        }
      }
      
      class HomeBody extends StatelessWidget {
         @override
         Widget build(BuildContext context) {
            print("HomeBody build");
            return MyCounterWidget();
         }
      }
      
      class MyCounterWidget extends StatefulWidget {
      
         MyCounterWidget() {
             print("執行了MyCounterWidget的構造方法");
         }
      
         @override
         State<StatefulWidget> createState() {
              print("執行了MyCounterWidget的createState方法");
              // 將創建的State返回
              return MyCounterState();
          }
      }
      
      class MyCounterState extends State<MyCounterWidget> {
           int counter = 0;
      
           MyCounterState() {
              print("執行MyCounterState的構造方法");
           }
      
           @override
           void initState() {
               super.initState();
               print("執行MyCounterState的init方法");
           }
      
           @override
           void didChangeDependencies() {
              // TODO: implement didChangeDependencies
              super.didChangeDependencies();
              print("執行MyCounterState的didChangeDependencies方法");
           }
      
           @override
           Widget build(BuildContext context) {
               print("執行執行MyCounterState的build方法");
               return Center(
                     child: Column(
                       mainAxisAlignment: MainAxisAlignment.center,
                       children: <Widget>[
                            Row(
                              mainAxisAlignment: MainAxisAlignment.center,
                              children: <Widget>[
                                RaisedButton(
                                   color: Colors.redAccent,
                                   child: Text("+1", style: TextStyle(fontSize: 18, color: Colors.white),),
                                  onPressed: () {
                                     setState(() {
                                         counter++;
                                     });
                                  },
                                ),
                               RaisedButton(
                                   color: Colors.orangeAccent,
                                   child: Text("-1", style: TextStyle(fontSize: 18, color: Colors.white),),
                                   onPressed: () {
                                       setState(() {
                                          counter--;
                                       });
                                   },
                               )
                            ],
                         ),
                        Text("當前計數:$counter", style: TextStyle(fontSize: 30),)
                    ],
                ),
            );
          }
      
         @override
         void didUpdateWidget(MyCounterWidget oldWidget) {
                super.didUpdateWidget(oldWidget);
                 print("執行MyCounterState的didUpdateWidget方法");
         }
      
         @override
         void dispose() {
            super.dispose();
            print("執行MyCounterState的dispose方法");
         }
      }
      

      打印結果如下:

      flutter: HomeBody build
      flutter: 執行了MyCounterWidget的構造方法
      flutter: 執行了MyCounterWidget的createState方法
      flutter: 執行MyCounterState的構造方法
      flutter: 執行MyCounterState的init方法
      flutter: 執行MyCounterState的didChangeDependencies方法
      flutter: 執行執行MyCounterState的build方法
      
      // 注意:Flutter會build所有的組件兩次(查了GitHub、Stack Overflow,目前沒查到原因)
      flutter: HomeBody build
      flutter: 執行了MyCounterWidget的構造方法
      flutter: 執行MyCounterState的didUpdateWidget方法
      flutter: 執行執行MyCounterState的build方法
      

      當我們改變狀態,手動執行setState方法后會打印如下結果:

      flutter: 執行執行MyCounterState的build方法
      
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,936評論 6 535
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,744評論 3 421
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,879評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,181評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,935評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,325評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,384評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,534評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,084評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,892評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,067評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,623評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,322評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,735評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,990評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,800評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,084評論 2 375