Flutter開發(fā) -- [01 - Hello World]

一. 第一個Flutter代碼

1.1. Hello World

1.1.1. Hello World的需求

做任何的開發(fā),我們都是從祖?zhèn)鞯?code>Hello World開始寇钉,那么現(xiàn)在我們的需求來了:

  • 在界面中心位置,顯示一個Hello World画饥;

1.1.2. Hello World的實現(xiàn)

下面茬贵,我們就動手開始編寫Hello World:

import 'package:flutter/material.dart';

main(List<String> args) {
  runApp(Text("Hello World", textDirection: TextDirection.ltr));
}

當(dāng)然摘完,上面的代碼我們已經(jīng)實現(xiàn)了在界面上顯示Hello World:

  • 但是沒有居中字體也有點小羹令;
  • 這些問題鲤屡,我們放到后面再來解決,先搞懂目前的幾行代碼福侈;

上面的代碼我們有一些比較熟悉酒来,有一些并不清楚是什么:

  • 比如我們知道Dart程序的入口都是main函數(shù),而Flutter是Dart編寫的肪凛,所以入口也是main函數(shù)堰汉;
  • 但是我們導(dǎo)入的Material是什么呢
  • 另外伟墙,我們在main函數(shù)中調(diào)用了一個runApp()函數(shù)又是什么呢翘鸭?

下面,我們對不認識的代碼進行一些分析戳葵。

1.2. 代碼分析

1.2.1. runApp和Widget

runApp是Flutter內(nèi)部提供的一個函數(shù)就乓,當(dāng)我們啟動一個Flutter應(yīng)用程序時就是從調(diào)用這個函數(shù)開始的

  • 我們可以點到runApp的源碼,查看到該函數(shù)
  • 我們暫時不分析具體的源碼(因為我發(fā)現(xiàn)過多的理論拱烁,對于初學(xué)者來說并不友好)
void runApp(Widget app) {
  ...省略代碼
}

該函數(shù)讓我們傳入一個東西:Widget生蚁?

我們先說Widget的翻譯

  • Widget在國內(nèi)有很多的翻譯;
  • 做過Android戏自、iOS等開發(fā)的人群邦投,喜歡將它翻譯成控件
  • 做過Vue擅笔、React等開發(fā)的人群志衣,喜歡將它翻譯成組件见芹;
  • 如果我們使用Google,Widget翻譯過來應(yīng)該是小部件蠢涝;
  • 沒有說哪種翻譯一定是對的玄呛,或者一定是錯的,但是我個人更傾向于小部件或者組件和二;

Widget到底什么東西呢徘铝?

  • 我們學(xué)習(xí)Flutter,從一開始就可以有一個基本的認識:Flutter中萬物皆Widget(萬物皆可盤)惯吕;
  • 在我們iOS或者Android開發(fā)中惕它,我們的界面有很多種類的劃分:應(yīng)用(Application)、視圖控制器(View Controller)废登、活動(Activity)淹魄、View(視圖)、Button(按鈕)等等堡距;
  • 但是在Flutter中甲锡,這些東西都是不同的Widget而已
  • 也就是我們整個應(yīng)用程序中所看到的內(nèi)容幾乎都是Widget羽戒,甚至是內(nèi)邊距的設(shè)置缤沦,我們也需要使用一個叫Padding的Widget來做;

runApp函數(shù)讓我們傳入的就是一個Widget:

  • 但是我們現(xiàn)在沒有Widget易稠,怎么辦呢缸废?
  • 我們可以導(dǎo)入Flutter默認已經(jīng)給我們提供的Material庫,來使用其中的很多內(nèi)置Widget驶社;

1.2.2. Material設(shè)計風(fēng)格

material是什么呢企量?

  • material是Google公司推行的一套設(shè)計風(fēng)格,或者叫設(shè)計語言亡电、設(shè)計規(guī)范等届巩;
  • 里面有非常多的設(shè)計規(guī)范,比如顏色逊抡、文字的排版姆泻、響應(yīng)動畫與過度填充等等冒嫡;
  • 在Flutter中高度集成了Material風(fēng)格的Widget拇勃;
  • 在我們的應(yīng)用中,我們可以直接使用這些Widget來創(chuàng)建我們的應(yīng)用(后面會用到很多)孝凌;

Text小部件分析

  • 我們可以使用Text小部件來完成文字的顯示方咆;
  • 我們發(fā)現(xiàn)Text小部件繼承自StatelessWidget,StatelessWidget繼承自Widget蟀架;
  • 所以我們可以將Text小部件傳入到runApp函數(shù)中
  • 屬性非常多瓣赂,但是我們已經(jīng)學(xué)習(xí)了Dart語法榆骚,所以你會發(fā)現(xiàn)只有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 {
    // ...省略代碼
}

1.3. 代碼改進

1.3.1. 改進界面樣式

我們發(fā)現(xiàn)現(xiàn)在的代碼并不是我們想要的最終結(jié)果:

  • 我們可能希望文字居中顯示,并且可以大一些苫纤;
  • 居中顯示: 需要使用另外一個Widget碉钠,Center
  • 文字大一些: 需要給Text文本設(shè)置一些樣式卷拘;

我們修改代碼如下:

  • 我們在Text小部件外層包裝了一個Center部件喊废,讓Text作為其child;
  • 并且栗弟,我們給Text組件設(shè)置了一個屬性:style污筷,對應(yīng)的值是TextStyle類型;
import 'package:flutter/material.dart';

main(List<String> args) {
  runApp(
    Center(
      child: Text(
        "Hello World",
        textDirection: TextDirection.ltr,
        style: TextStyle(fontSize: 36),
      ),
    )
  );
}

1.3.2. 改進界面結(jié)構(gòu)

目前我們雖然可以顯示HelloWorld乍赫,但是我們發(fā)現(xiàn)最底部的背景是黑色瓣蛀,并且我們的頁面并不夠結(jié)構(gòu)化。

  • 正常的App頁面應(yīng)該有一定的結(jié)構(gòu)耿焊,比如通常都會有導(dǎo)航欄揪惦,會有一些背景顏色

在開發(fā)當(dāng)中,我們并不需要從零去搭建這種結(jié)構(gòu)化的界面罗侯,我們可以使用Material庫,直接使用其中的一些封裝好的組件來完成一些結(jié)構(gòu)的搭建溪猿。

我們通過下面的代碼來實現(xiàn):

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

  • 這意味著整個應(yīng)用我們都會采用MaterialApp風(fēng)格的一些東西钩杰,方便我們對應(yīng)用的設(shè)計,并且目前我們使用了其中兩個屬性诊县;
  • title:這個是定義在Android系統(tǒng)中打開多任務(wù)切換窗口時顯示的標(biāo)題讲弄;(暫時可以不寫)
  • home:是該應(yīng)用啟動時顯示的頁面,我們傳入了一個Scaffold依痊;

Scaffold是什么呢避除?

  • 翻譯過來是腳手架,腳手架的作用就是搭建頁面的基本結(jié)構(gòu)胸嘁;
  • 所以我們給MaterialApp的home屬性傳入了一個Scaffold對象瓶摆,作為啟動顯示的Widget;
  • Scaffold也有一些屬性性宏,比如appBarbody群井;
  • appBar是用于設(shè)計導(dǎo)航欄的,我們傳入了一個title屬性毫胜;
  • body是頁面的內(nèi)容部分书斜,我們傳入了之前已經(jīng)創(chuàng)建好的Center中包裹的一個Text的Widget诬辈;

1.3.3. 進階案例實現(xiàn)

我們可以讓界面中存在更多的元素:

  • 寫到這里的時候,你可能已經(jīng)發(fā)現(xiàn)嵌套太多了荐吉,不要著急焙糟,我們后面會對代碼重構(gòu)的
import 'package:flutter/material.dart';

main(List<String> args) {
  runApp(
    MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text("CODERWHY"),
        ),
        body: Center(
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Checkbox(
                value: true,
                onChanged: (value) => print("Hello World")),
              Text(
                "同意協(xié)議",
                textDirection: TextDirection.ltr,
                style: TextStyle(fontSize: 20),
              )
            ],
          ),
        ),
      ),
    )
  );
}

1.4. 代碼重構(gòu)

1.4.1. 創(chuàng)建自己的Widget

很多學(xué)習(xí)Flutter的人,都會被Flutter的嵌套勸退样屠,當(dāng)代碼嵌套過多時穿撮,結(jié)構(gòu)很容易看不清晰。

這里有兩點我先說明一下:

  • 1瞧哟、Flutter整個開發(fā)過程中就是形成一個Widget樹混巧,所以形成嵌套是很正常的。
  • 2勤揩、關(guān)于Flutter的代碼縮進咧党,更多開發(fā)中我們使用的是2個空格(前端開發(fā)2個空格居多,你喜歡4個也沒問題)

但是陨亡,我們開發(fā)一個這么簡單的程序就出現(xiàn)如此多的嵌套傍衡,如果應(yīng)用程序更復(fù)雜呢?

  • 我們可以對我們的代碼進行封裝负蠕,將它們封裝到自己的Widget中蛙埂,創(chuàng)建自己的Widget;

如何創(chuàng)建自己的Widget呢遮糖?

  • 在Flutter開發(fā)中绣的,我們可以繼承自StatelessWidget或者StatefulWidget來創(chuàng)建自己的Widget類;
  • StatelessWidget: 沒有狀態(tài)改變的Widget欲账,通常這種Widget僅僅是做一些展示工作而已屡江;
  • StatefulWidget: 需要保存狀態(tài),并且可能出現(xiàn)狀態(tài)改變的Widget赛不;

在上面的案例中對代碼的重構(gòu)惩嘉,我們使用StatelessWidget即可,所以我們接下來學(xué)習(xí)一下如果利用StatelessWidget來對我們的代碼進行重構(gòu)踢故;

StatefulWidget我們放到后面的一個案例中來學(xué)習(xí)文黎;

1.4.2. StatelessWidget

StatelessWidget通常是一些沒有狀態(tài)(State,也可以理解成data)需要維護的Widget:

  • 它們的數(shù)據(jù)通常是直接寫死(放在Widget中的數(shù)據(jù)殿较,必須被定義為final耸峭,為什么呢?我在下一個章節(jié)講解StatefulWidget會講到)斜脂;
  • 從parent widget中傳入的而且一旦傳入就不可以修改抓艳;
  • 從InheritedWidget獲取來使用的數(shù)據(jù)(這個放到后面會講解);

我們來看一下創(chuàng)建一個StatelessWidget的格式:

  • 1、讓自己創(chuàng)建的Widget繼承自StatelessWidget玷或;
  • 2儡首、StatelessWidget包含一個必須重寫的方法:build方法;
class MyStatelessWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return <返回我們的Widget要渲染的Widget偏友,比如一個Text Widget>;
  }
}

build方法的解析:

  • Flutter在拿到我們自己創(chuàng)建的StatelessWidget時蔬胯,就會執(zhí)行它的build方法;
  • 我們需要在build方法中告訴Flutter位他,我們的Widget希望渲染什么元素氛濒,比如一個Text Widget;
  • StatelessWidget沒辦法主動去執(zhí)行build方法鹅髓,當(dāng)我們使用的數(shù)據(jù)發(fā)生改變時舞竿,build方法會被重新執(zhí)行;

build方法什么情況下被執(zhí)行呢窿冯?:

  • 1骗奖、當(dāng)我們的StatelessWidget第一次被插入到Widget樹中時(也就是第一次被創(chuàng)建時);
  • 2醒串、當(dāng)我們的父Widget(parent widget)發(fā)生改變時执桌,子Widget會被重新構(gòu)建;
  • 3芜赌、如果我們的Widget依賴InheritedWidget的一些數(shù)據(jù)仰挣,InheritedWidget數(shù)據(jù)發(fā)生改變時;

1.4.3. 重構(gòu)案例代碼

現(xiàn)在我們就可以通過StatelessWidget來對我們的代碼進行重構(gòu)了

  • 因為我們的整個代碼都是一些數(shù)據(jù)展示缠沈,沒有數(shù)據(jù)的改變膘壶,使用StatelessWidget即可;
  • 另外洲愤,為了體現(xiàn)更好的封裝性香椎,我對代碼進行了兩層的拆分,讓代碼結(jié)構(gòu)看起來更加清晰禽篱;(具體的拆分方式,我會在后面的案例中不斷的體現(xiàn)出來馍惹,目前我們先拆分兩層)

重構(gòu)后的代碼如下:

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(
            "同意協(xié)議",
            textDirection: TextDirection.ltr,
            style: TextStyle(fontSize: 20),
          )
        ],
      ),
    );
  }
}

二. 案例練習(xí)

2.1. 案例最終效果

我們先來看一下案例的最終展示效果:

2.2. 自定義Widget

在我們的案例中躺率,很明顯一個產(chǎn)品的展示就是一個大的Widget,這個Widget包含如下Widget:

  • 標(biāo)題的Widget:使用一個Text Widget完成万矾;
  • 描述的Widget:使用一個Text Widget完成悼吱;
  • 圖片的Widget:使用一個Image Widget完成;
  • 上面三個Widget要垂直排列良狈,我們可以使用一個Column的Widget(上一個章節(jié)中我們使用了一次Row是水平排列的)

另外后添,三個展示的標(biāo)題、描述薪丁、圖片都是不一樣的遇西,所以我們可以讓Parent Widget來決定內(nèi)容:

  • 創(chuàng)建三個成員變量保存父Widget傳入的數(shù)據(jù)
class ProductItem extends StatelessWidget {
  final String title;
  final String desc;
  final String imageURL;

  ProductItem(this.title, this.desc, this.imageURL);

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        Text(title, style: TextStyle(fontSize: 24)),
        Text(desc, style: TextStyle(fontSize: 18)),
        Image.network(imageURL)
      ],
    );
  }
}

2.3. 列表數(shù)據(jù)展示

現(xiàn)在我們就可以創(chuàng)建三個ProductItem來讓他們展示了:

  • MyApp和上一個章節(jié)是一致的馅精,沒有任何改變;
  • HomeContent中粱檀,我們使用了一個Column洲敢,因為我們創(chuàng)建的三個ProductItem是垂直排列的
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        primaryColor: Colors.blueAccent
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text("CODERWHY"),
        ),
        body: HomeContent(),
      ),
    );
  }
}

class HomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        ProductItem("Apple1", "Macbook Product1", "https://tva1.sinaimg.cn/large/006y8mN6gy1g72j6nk1d4j30u00k0n0j.jpg"),
        ProductItem("Apple2", "Macbook Product2", "https://tva1.sinaimg.cn/large/006y8mN6gy1g72imm9u5zj30u00k0adf.jpg"),
        ProductItem("Apple3", "Macbook Product3", "https://tva1.sinaimg.cn/large/006y8mN6gy1g72imqlouhj30u00k00v0.jpg"),
      ],
    );
  }
}

運行效果如下:

  • 錯誤信息:下面出現(xiàn)了黃色的斑馬線;
  • 這是因為在Flutter的布局中茄蚯,內(nèi)容是不能超出屏幕范圍的压彭,當(dāng)超出時不會自動變成滾動效果,而是會報下面的錯誤渗常;

如何可以解決這個問題呢壮不?

  • 我們將Column換成ListView即可;
  • ListView可以讓自己的子Widget變成滾動的效果皱碘;

2.4. 案例細節(jié)調(diào)整

2.4.1. 界面整體邊距

如果我們希望整個內(nèi)容距離屏幕的邊緣有一定的間距询一,怎么做呢?

  • 我們需要使用另外一個Widget:Padding尸执,它有一個padding屬性用于設(shè)置邊距大屑铱;
  • 沒錯如失,設(shè)置內(nèi)邊距也是使用Widget绊诲,這個Widget就是Padding;

2.4.2. 商品內(nèi)邊距和邊框

我們現(xiàn)在希望給所有的商品也添加一個內(nèi)邊距褪贵,并且還有邊框掂之,怎么做呢?

  • 我們可以使用一個Container的Widget脆丁,它里面有padding屬性世舰,并且可以通過decoration來設(shè)置邊框;
  • Container我們也會在后面詳細來講槽卫,我們先用起來跟压;

2.4.3. 文字圖片的間距

我們希望給圖片和文字之間添加一些間距,怎么做呢歼培?

  • 方式一:給圖片或者文字添加一個向上的內(nèi)邊距或者向下的內(nèi)邊距震蒋;
  • 方式二:使用SizedBox的Widget,設(shè)置一個height屬性躲庄,可以增加一些距離查剖;

2.5. 最終實現(xiàn)代碼

最后,我給出最終實現(xiàn)代碼:

import 'package:flutter/material.dart';

main(List<String> args) {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        primaryColor: Colors.blueAccent
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text("CODERWHY"),
        ),
        body: HomeContent(),
      ),
    );
  }
}

class HomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: ListView(
        children: <Widget>[
          ProductItem("Apple1", "Macbook Product1", "https://tva1.sinaimg.cn/large/006y8mN6gy1g72j6nk1d4j30u00k0n0j.jpg"),
          ProductItem("Apple2", "Macbook Product2", "https://tva1.sinaimg.cn/large/006y8mN6gy1g72imm9u5zj30u00k0adf.jpg"),
          ProductItem("Apple3", "Macbook Product3", "https://tva1.sinaimg.cn/large/006y8mN6gy1g72imqlouhj30u00k00v0.jpg"),
        ],
      ),
    );
  }
}

class ProductItem extends StatelessWidget {
  final String title;
  final String desc;
  final String imageURL;

  ProductItem(this.title, this.desc, this.imageURL);

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.all(20),
      decoration: BoxDecoration(
        border: Border.all()
      ),
      child: Column(
        children: <Widget>[
          Text(title, style: TextStyle(fontSize: 24)),
          Text(desc, style: TextStyle(fontSize: 18)),
          SizedBox(height: 10,),
          Image.network(imageURL)
        ],
      ),
    );
  }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末噪窘,一起剝皮案震驚了整個濱河市笋庄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖直砂,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件菌仁,死亡現(xiàn)場離奇詭異,居然都是意外死亡哆键,警方通過查閱死者的電腦和手機掘托,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來籍嘹,“玉大人闪盔,你說我怎么就攤上這事∪枋浚” “怎么了泪掀?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長颂碘。 經(jīng)常有香客問我异赫,道長,這世上最難降的妖魔是什么头岔? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任塔拳,我火速辦了婚禮,結(jié)果婚禮上峡竣,老公的妹妹穿的比我還像新娘靠抑。我一直安慰自己,他們只是感情好适掰,可當(dāng)我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布颂碧。 她就那樣靜靜地躺著,像睡著了一般类浪。 火紅的嫁衣襯著肌膚如雪载城。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天费就,我揣著相機與錄音诉瓦,去河邊找鬼。 笑死力细,一個胖子當(dāng)著我的面吹牛垦搬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播艳汽,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼对雪!你這毒婦竟也來了河狐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎馋艺,沒想到半個月后栅干,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡捐祠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年碱鳞,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片踱蛀。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡窿给,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出率拒,到底是詐尸還是另有隱情崩泡,我是刑警寧澤,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布猬膨,位于F島的核電站角撞,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏勃痴。R本人自食惡果不足惜谒所,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望沛申。 院中可真熱鬧劣领,春花似錦、人聲如沸污它。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽衫贬。三九已至德澈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間固惯,已是汗流浹背梆造。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留葬毫,地道東北人镇辉。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像贴捡,于是被迫代替她去往敵國和親忽肛。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,864評論 2 354