Flutter Router路由封装

1、Flutter Router路由封装
    路由插件: fluro
1.1 在 pubspec.yaml 中加入下面代码,并获取该依赖包
    fluro: "^1.6.3"
1.2 在 lib 文件夹下新建一个 router 文件夹,并新建 router.dartrouter_handler.dart 两个文件

1.2.1 router_handler.dart 文件

    import 'package:fluro/fluro.dart';          // 引入路由包依赖文件
    import 'package:flutter/cupertino.dart';
    import 'package:new_flutter/pages/page_one.dart';   //  page_one为第一个页面
    import 'package:new_flutter/pages/page_two.dart';   //  page_two为第一个页面

    // 创建每个路由的 Handler函数
    var pageOne =  new Handler(
        handlerFunc: (BuildContext context, Map<String,List<String>>params ){
            return  PageOne();   //  PageOne 为 page_one 页面的 StatelessWidget
        }
    );
    var pageTwo =  new Handler(
        handlerFunc: (BuildContext context, Map<String,List<String>>params  ){
            return  PageTwo(); //  PageTwo 为 page_two 页面的 StatelessWidget
        }
    );

1.2.2 router.dart 文件

    import 'package:flutter/cupertino.dart';
    import 'package:fluro/fluro.dart';                          // 引入路由包依赖文件
    import 'package:new_flutter/router/router_handler.dart';    // 每个页面路由对应的 Handler 函数文件

    // 定义路由配置类
    class Routers {
        static String root = '/';
        //路由配置
        static void configRouters(Router router){
            //找不到路由
            router.notFoundHandler = new Handler(
                handlerFunc: (BuildContext context,Map<String,List<String>> params){
                    print('ERROR====>ROUTE WAS NOT FONUND!!!');
                    return;
                }
            );
            //整体配置
            router.define('/pageOne', handler: pageOne);  //  page_one 页面路由
            router.define('/pageTwo', handler: pageTwo);  //  page_two 页面路由
        }
    }
1.3 在 main.dart 中引入路由信息,并初始化
    import 'package:fluro/fluro.dart';                        // 引入路由包依赖文件 
    import 'package:flutter/material.dart';
    import 'package:new_flutter/pages/page_one.dart';         //  这里 page_one 作为应用打开页面
    import 'package:new_flutter/router/router.dart';          //  路由信息文件

    void main() => runApp(new MyApp());

    class MyApp extends StatelessWidget {
        final GlobalKey<NavigatorState> navigatorKey = GlobalKey();
        @override
        Widget build(BuildContext context) {
            //路由初始化
            final router = Router(); 
            Routers.configRouters(router);

            return new MaterialApp(
                title: 'Flutter App',               // 设备用于为用户识别应用程序的单行描述   
                theme: ThemeData(                   // 应用程序小部件使用的颜色。
                    primaryColor: Color.fromRGBO(222, 225, 231, 1)
                ),
                color: Colors.red,                  // 在操作系统界面中应用程序使用的主色。
                home: PageOne(),                    // 应用程序默认路由的小部件,用来定义当前应用打开的时候,所显示的界面    
                navigatorKey: navigatorKey,         // 在构建导航器时使用的键
                onGenerateRoute: router.generator,  // 应用程序导航到指定路由时使用的路由生成器回调   
            );
        }
    }
1.4 创建页面 page_one 和 page_two
1.4.1 page_one.dart (这里我做了一个按钮,点击跳转至 page_two 页面)
    import 'package:flutter/material.dart';
    class PageOne extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
        return  MaterialApp(
            home: new Scaffold(
                appBar: new AppBar(     // 头部导航栏
                    title: new Text('我的 Flutter'),
                ),
                body: new Center(
                    child: Column(
                        children:[
                            OutlineButton(
                                child: Text("normal"),
                                onPressed: () {
                                    // pageOne 跳转到 pageTwo页面 ,  
                                    Navigator.of(context).pushNamed(
                                        '/pageTwo',                 //  router.dart 中定义的路由访问链接
                                        arguments: { 'id': 1}       //  arguments 传递的参数
                                    );
                                },
                            )
                        ]
                    ),
                ),
            ),
        );
    }
}
1.4.2 page_two.dart (这里我接收从 page_one 页面传递过来的参数)
    import 'package:flutter/material.dart';
    class PageTwo extends StatelessWidget {
        @override
        Widget build(BuildContext context) {

            // 获取路由传参
            print( ModalRoute.of(context).settings.arguments );

            return  MaterialApp(
                home: new Scaffold(
                    appBar: new AppBar(
                        title: new Text('page Two'),
                    ),
                    body: new Center(
                        child: Text('page Two'),
                    ),
                ),
            );
        }
    }
注意:
    MaterialApp({
        Key key,
        this.title = '',                                // 设备用于为用户识别应用程序的单行描述
        this.home,                                      // 应用程序默认路由的小部件,用来定义当前应用打开的时候,所显示的界面
        this.color,                                     // 在操作系统界面中应用程序使用的主色。
        this.theme,                                     // 应用程序小部件使用的颜色。
        this.routes = const <String, WidgetBuilder>{},  // 应用程序的顶级路由表
        this.navigatorKey,                              // 在构建导航器时使用的键。
        this.initialRoute,                              // 如果构建了导航器,则显示的第一个路由的名称
        this.onGenerateRoute,                           // 应用程序导航到指定路由时使用的路由生成器回调
        this.onUnknownRoute,                            // 当 onGenerateRoute 无法生成路由(initialRoute除外)时调用
        this.navigatorObservers = const <NavigatorObserver>[], // 为该应用程序创建的导航器的观察者列表
        this.builder,                                   // 用于在导航器上面插入小部件,但在由WidgetsApp小部件创建的其他小部件下面插入小部件,或用于完全替换导航器
        this.onGenerateTitle,                           // 如果非空,则调用此回调函数来生成应用程序的标题字符串,否则使用标题。
        this.locale,                                    // 此应用程序本地化小部件的初始区域设置基于此值。
        this.localizationsDelegates,                    // 这个应用程序本地化小部件的委托。
        this.localeListResolutionCallback,              // 这个回调负责在应用程序启动时以及用户更改设备的区域设置时选择应用程序的区域设置。
        this.localeResolutionCallback, 
        this.supportedLocales = const <Locale>[Locale('en', 'US')],     // 此应用程序已本地化的地区列表 
        this.debugShowMaterialGrid = false,             // 打开绘制基线网格材质应用程序的网格纸覆盖
        this.showPerformanceOverlay = false,            // 打开性能叠加
        this.checkerboardRasterCacheImages = false,     // 打开栅格缓存图像的棋盘格
        this.checkerboardOffscreenLayers = false,       // 打开渲染到屏幕外位图的图层的棋盘格
        this.showSemanticsDebugger = false,             // 打开显示框架报告的可访问性信息的覆盖
        this.debugShowCheckedModeBanner = true,         // 在选中模式下打开一个小的“DEBUG”横幅,表示应用程序处于选中模式
    }) 
1.5 自定义动画路由

很多时候,我们的项目中会有一些特殊的页面切换动效,此时我们就需要自定义路由动画,flutterPageRouteBuilder 提供了一个 Animation 对象,Animation 能够通过结合 Tween 以及 Curve 对象来自定义路由转换动画

PageRouteBuilder 属性

    PageRouteBuilder({
        RouteSettings settings,                 // 路由基本信息
        @required this.pageBuilder,             // 创建跳转的路由页面
        this.transitionsBuilder = _defaultTransitionsBuilder,           // 自定义的转场效果
        this.transitionDuration = const Duration(milliseconds: 300),    // 转场动画的持续时间
        this.opaque = true,                    // 是否遮挡整个屏幕
        this.barrierDismissible = false,
        this.barrierColor,                    // 路由回退时页面动画阴影颜色
        this.barrierLabel,
        this.maintainState = true,            // 默认情况下,当入栈一个新路由时,原来的路由仍然会被保存在内存中,如果想在路由没用的时候释放其所占用的所有资源,可以设maintainState为false
        bool fullscreenDialog = false,        // 表示新的路由页面是否是一个全屏的模态对话框,在iOS中,如果fullscreenDialog为true,新页面将会从屏幕底部滑入(而不是水平方向)。
    })
1.5.1 搭建一个 PageRouteBuilder
    /*
        *  Widget nextPage  下一个路由组件
        *  Tween  tween     定义的页面动画过程
    */
    PageRouteBuilder(
        // 设置路由基本信息Settings,可通过它获取当前路由、上个页面路由
        settings: new RouteSettings(
        name: '$nextPage'
        ),
        // 用来生成要显示的Widget
        pageBuilder: (context,animation,secondaryAnimation) => nextPage,
        // 设置转场动画的效果
        transitionsBuilder: (context, animation, secondaryAnimation, child){
            return SlideTransition(
            position: animation.drive(
                tween.chain(CurveTween( curve: Curves.ease))
            ),
            child: child
            );
        },
        // 设置了动画时长
        transitionDuration:const Duration(milliseconds: 600)
    ); 
1.5.2 创建 Tween

为了使新页面从底部动画出来,它应该从 Offset(0,1) 到 Offset(0, 0) 进行动画 Offset( dx, dy )。(通常我们会使用 Offset.zero 构造器。)在这个情况下,对于 ‘FractionalTranslation’ widget 来说偏移量是一个 2D 矢量值。将 dy 参数设为 1,这代表在竖直方向上切换整个页面的高度。

    //  动画Map表
    Map tweenObj = {
        'rightIn' : Tween(  begin: Offset(1,0), end: Offset.zero ),
        'leftIn' : Tween(  begin: Offset(-1,0), end: Offset.zero ),
        'topIn' : Tween(  begin: Offset(0,-1), end: Offset.zero ),
        'bottomIn' : Tween(  begin: Offset(0,1), end: Offset.zero ),
    };
1.5.3 使用 AnimatedWidget

Flutter 有一堆继承自 AnimatedWidget 的 widget,它们能够在动画的值发生改变时自动重建自己。举个例子,SlideTransition 拿到一个 Animation<Offset> 并在动画改变时使用 FractionalTranslation widget 转换其子级。

AnimatedWidget 返回了一个带有 Animation<Offset>SlideTransition,以及 child widget:

    // 生成一个从右边进入的动画
    var offsetAnimation = animation.drive(tweenObj['rightIn']);
1.5.4 使用 CurveTween

Curves 类提供了一个提前定义的用法相似的 curves 。例如,Curves.easeOut 将会让动画开始很快结束很慢。

    var curve = Curves.ease;
    var curveTween = CurveTween(curve: curve);
1.5.5 结合两个 Tween

连接两个 Tween 需要用 chain() :

    var curve = Curves.ease;
    var tween = tweenObj['rightIn'].chain(CurveTween(curve: curve));
1.5.6 完整的项目代码实例 -- 封装自定义路由跳转函数
    // 引入项目包
    import 'package:flutter/material.dart';
    import 'package:fluro/fluro.dart';

    //  路由跳转公共函数
    // ignore: missing_return
    Future<Null> jumpPage(BuildContext context, String method, Widget nextPage, String animate){
        //  动画Map表
        Map tweenObj = {
            'rightIn' : Tween(  begin: Offset(1,0), end: Offset.zero ),
            'leftIn' : Tween(  begin: Offset(-1,0), end: Offset.zero ),
            'topIn' : Tween(  begin: Offset(0,-1), end: Offset.zero ),
            'bottomIn' : Tween(  begin: Offset(0,1), end: Offset.zero ),
        };
        // 定义 PageRouteBuilder 函数
        Route _createRoutee(Tween tween){
            // 利用 PageRouteBuilder 来创建 Route
            return PageRouteBuilder(
                // 设置路由基本信息Settings,可通过它获取当前路由、上个页面路由
                settings: new RouteSettings(
                    name: '$nextPage'
                ),
                // 用来生成要显示的Widget
                pageBuilder: (context,animation,secondaryAnimation) => nextPage,
                // 设置转场动画的效果
                transitionsBuilder: (context, animation, secondaryAnimation, child){
                    return SlideTransition(
                        position: animation.drive(
                        tween.chain(CurveTween( curve: Curves.ease))
                        ),
                        child: child
                    );
                },
                // 设置了动画时长
                transitionDuration:const Duration(milliseconds: 600)
            );
        }
        switch(method){
            // 将设置的router信息推送到Navigator上,实现页面跳转。
            case 'push':
                Navigator.push(context, _createRoutee(tweenObj[animate]));
            break;
            // 路由替换。
            case 'pushReplacement':
                Navigator.pushReplacement(context, _createRoutee(tweenObj[animate]));
            break;
            // 从Navigator中删除路由,同时执行Route.dispose操作。
            case 'removeRoute':
                Navigator.removeRoute(context, _createRoutee(tweenObj[animate]));
            break;
            // 将给定路由推送到Navigator,删除先前的路由,直到该函数的参数predicate返回true为止。
            case 'pushAndRemoveUntil':
                Navigator.pushAndRemoveUntil(context, _createRoutee(tweenObj[animate]), (route) => false);
            break;
            // 从Navigator中删除路由,同时执行Route.dispose操作。
            case 'removeRoute':
                Navigator.removeRoute(context, _createRoutee(tweenObj[animate]));
            break;
            // 导航到新页面,或者返回到上个页面。
            case 'pop':
                Navigator.pop(context,_createRoutee(tweenObj[animate]));
            break;
        }
    }
1.5.7 项目中使用封装的路由跳转函数做跳转

在需要用到跳转的组件中引入封装的文件,并使用跳转函数 jumpPage

    // 引入依赖包
    import 'package:new_flutter/utils/util.dart';           // 封装的路由跳转函数的文件
    import 'package:new_flutter/pages/next_page.dart';     // 需要跳转到的组件

    onPressed: () async{
        /*
            *   push 路由跳转的方法名
            *   NextPage  需要跳转的下一个路由组件
            *   topIn 需要用到的路由动画名
        */
        jumpPage(context, 'push', NextPage(), 'topIn');
    },
1.6 路由监听 RouteObserver

当页面路由进行路由跳转时,RouteObserver 就会通知订阅者。例如,每当用户从当前页面路线导航到另一个页面路线时,RouteObserver<PageRoute> 都会通知订阅的 RouteAware 对其 Route 状态的更改。

在项目中添加全局路由监听

main.dart 文件中添加全局路由监听逻辑:

1、将 RouteObserver 注册为全局导航观察器

    // 全局的路由监听者,可在需要的widget中添加,应该放到一个全局定义的文件中
    final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();

2、定义路由监听类

    class MyRouterLister extends NavigatorObserver{
        @override
            // push 路由监听
            void didPush(Route route, Route previousRoute) {
                super.didPush(route, previousRoute);
                var previousName = '';
                if (previousRoute == null) {
                    previousName = 'null';
                }else {
                    previousName = previousRoute.settings.name;
                }
                print('添加路由:' + route.settings.name + '  Previous:' + previousName);
            }

            @override
            // pop 路由监听
            void didPop(Route route, Route previousRoute) {
                super.didPop(route, previousRoute);

                var previousName = '';
                if (previousRoute == null) {
                    previousName = 'null';
                }else {
                    previousName = previousRoute.settings.name;
                }
                print('pop路由:' + route.settings.name + '  Previous:' + previousName);
            }

            @override
            // remove 路由监听
            void didRemove(Route route, Route previousRoute) {
                super.didRemove(route, previousRoute);

                var previousName = '';
                if (previousRoute == null) {
                    previousName = 'null';
                }else {
                    previousName = previousRoute.settings.name;
                }
                print('remove路由:' + route.settings.name + '  Previous:' + previousName);
            }
            @override
            // replace 路由监听
            void didReplace({Route newRoute,Route oldRoute}) {
                super.didReplace();
                var oldName = '';
                    if (oldRoute == null) {
                    oldName = 'null';
                    }else {
                    oldName = oldRoute.settings.name;
                    }
                    print('replace路由:' + newRoute.settings.name + '  old:' + oldName);
                }
        }

3、把 routeObserver 这个东西传递给 MaterialApp 的 navigatorObservers ( 其实最终给到了 Navigator , MaterialApp widget 只是包裹了 Navigator )

    final GlobalKey<NavigatorState> navigatorKey = GlobalKey();
    final router = Router(); 
    MaterialApp(
        title: 'Flutter App',               // 设备用于为用户识别应用程序的单行描述   
        theme: ThemeData(                   // 应用程序小部件使用的颜色。
            primaryColor: Color.fromRGBO(222, 225, 231, 1)
        ),
        home: PageOne(),                    // 首页   
        navigatorKey: navigatorKey,         // 在构建导航器时使用的键
        onGenerateRoute:  router.generator, // 应用程序导航到指定路由时使用的路由生成器回调   
        navigatorObservers: [MyRouterLister(),routeObserver]
    );

路由监听完整 main.dart 文件代码

    import 'package:fluro/fluro.dart';
    import 'package:flutter/material.dart';
    import 'package:new_flutter/router/router.dart';    // 路由配置文件  
    import 'package:new_flutter/pages/page_one.dart';  // PageOne 组件文件

    void main() => runApp(new MyApp());

    class MyApp extends StatelessWidget {
        final GlobalKey<NavigatorState> navigatorKey = GlobalKey();
        @override
        Widget build(BuildContext context) {
            //路由初始化
            final router = Router(); 
        // 全局的路由监听者,可在需要的widget中添加,应该放到一个全局定义的文件中
        final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
        // Routers 为路由配置文件中的类
        Routers.configRouters(router);

        Application.router = router;
            return new MaterialApp(
                title: 'Flutter App',               // 设备用于为用户识别应用程序的单行描述   
                theme: ThemeData(                   // 应用程序小部件使用的颜色。
                    primaryColor: Color.fromRGBO(222, 225, 231, 1)
                ),
                color: Colors.red,                  // 在操作系统界面中应用程序使用的主色。
                home: PageOne(),                    // 应用程序默认路由的小部件,用来定义当前应用打开的时候,所显示的界面    
                navigatorKey: navigatorKey,         // 在构建导航器时使用的键
                onGenerateRoute:  Application.router.generator, // 应用程序导航到指定路由时使用的路由生成器回调   
                navigatorObservers: [MyRouterLister(),routeObserver],
            );
        }
    }

    class MyRouterLister extends NavigatorObserver{
        @override
        // push 路由监听
        void didPush(Route route, Route previousRoute) {
            super.didPush(route, previousRoute);
            var previousName = '';
            if (previousRoute == null) {
                previousName = 'null';
            }else {
                previousName = previousRoute.settings.name;
            }
            print('添加路由:' + route.settings.name + '  Previous:' + previousName);
        }

        @override
        // pop 路由监听
        void didPop(Route route, Route previousRoute) {
            super.didPop(route, previousRoute);
            var previousName = '';
            if (previousRoute == null) {
                previousName = 'null';
            }else {
                previousName = previousRoute.settings.name;
            }
            print('pop路由:' + route.settings.name + '  Previous:' + previousName);
        }

        @override
        // remove 路由监听
        void didRemove(Route route, Route previousRoute) {
            super.didRemove(route, previousRoute);
            var previousName = '';
            if (previousRoute == null) {
                previousName = 'null';
            }else {
                previousName = previousRoute.settings.name;
            }
            print('remove路由:' + route.settings.name + '  Previous:' + previousName);
        }
        @override
        // replace 路由监听
        void didReplace({Route newRoute,Route oldRoute}) {
            super.didReplace();
            var oldName = '';
            if (oldRoute == null) {
            oldName = 'null';
            }else {
            oldName = oldRoute.settings.name;
            }
            print('replace路由:' + newRoute.settings.name + '  old:' + oldName);
        }
    }
    // 路由静态化
    class Application {
        static Router router;
    }
注意: 当我们使用自定义路由( PageRouteBuilder )的时候,若有拿取路由栈中路由信息时,必须要设置 Settings ,否则会出现路由丢失,路由信息都为 Null
1.7 Navigator 路由方法总结

Navigator 是一个路由管理的组件,它提供了打开和退出路由页方法。Navigator 通过一个栈来管理活动路由集合

    push                将设置的router信息推送到Navigator上,实现页面跳转。

    of                  主要是获取Navigator最近的Widget。

    pop                 导航到新页面,或者返回到上个页面。

    canPop              判断是否可以导航到新页面

    maybePop            可能会导航到新页面

    popAndPushNamed     指定一个路由路径,并导航到新页面。

    popUntil            反复执行pop 直到该函数的参数predicate返回true为止。

    pushAndRemoveUntil  将给定路由推送到Navigator,删除先前的路由,直到该函数的参数predicate返回true为止。

    pushNamedAndRemoveUntil     将命名路由推送到Navigator,删除先前的路由,直到该函数的参数predicate返回true为止。

    pushNamed           将命名路由推送到Navigator。

    pushReplacement     路由替换。

    pushReplacementNamed    替换路由操作。推送一个命名路由到Navigator,新路由完成动画之后处理上一个路由。

    removeRoute         从Navigator中删除路由,同时执行Route.dispose操作。

    removeRouteBelow    从Navigator中删除路由,同时执行Route.dispose操作,要替换的路由是传入参数anchorRouter里面的路由。

    replace             将Navigator中的路由替换成一个新路由。

    replaceRouteBelow   将Navigator中的路由替换成一个新路由,要替换的路由是是传入参数anchorRouter里面的路由。
注意: 由于flutter 定义的历史路由栈 _history 为私有变量,不允许外部访问,因此 Flutter 中拿不到历史路由栈
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343