Flutter 之 Getx

1、介绍

当我们想在一个或多个页面/组件/widget 之间共享状态/数据,我们可以使用 Flutter 种的状态管理来实现。目前 Flutter 种的状态管理方案有 redux,bloc,state,provider,Getx。
provider 是官方提供的状态管理方案,主要功能就是状态管理;Getx 是第三方状态管理插件,不仅具有状态管理功能,还具有路由管理,主体管理,国际化多语言管理,obx 局部更新,网络请求,数据验证等功能,相比于其他插件,具有简单,功能强大,高性能。

2、Getx

Getx 是 Flutter 上的一个轻量且强大的解决方案,Getx 为我们提供了高性能的状态管理、智能的依赖注入和便捷的路由管理。
1、Getx 有3个基本原则:
性能:Getx 专注于性能和最小资源消耗。GetX 打包后的 apk 占用大小和运行时的内存占用与其他状态管理插件不相上下。
效率:Getx 的语法非常简捷,并保持了极高的性能,能极大缩短你的开发时长。
结构:Getx 可以将界面、逻辑、依赖和路由完全解耦,用起来更清爽,逻辑更清晰,代码更容易维护 。
2、Getx 并不臃肺,却很轻量。如果你只使用状态管理,只有状态管理模块会被编译,其他没用到的东西都不会被编译到你的代码中。它拥有众多的功能,但这些功能都在独立的容器中,只有在使用后才会启动。
3、Getx 有一个庞大的生态系统,能够在 Android、ios、Web、Mac、Linux、Windows 和你的服务器上用同样的代码运行。通过 GetServer 可以在你的后端完全重用你在前端写的代码。

3、配置

参考文档 >>>
1、pubspec.yaml 导入依赖库

dependencies:
  get: ^4.6.5

2、引入头文件

import 'package:get/get.dart';

3、将项目中 MaterialApp() 方法改为 GetMaterialApp() 方法。

4、Getx 的状态管理

1、 Get.defaultDialog

defaultDialog 属性

Future<T?> defaultDialog<T>({
  String title = "Alert",    //弹框标题
  EdgeInsetsGeometry? titlePadding,    //标题内边距,默认(EdgeInsets.all(8))
  TextStyle? titleStyle,    //标题样式
  Widget? content,    //弹框内容,该属性设置后 middleText 将无效
  EdgeInsetsGeometry? contentPadding,    //内容边距,默认(EdgeInsets.all(8))
  void Function()? onConfirm,    //确认按钮回调
  void Function()? onCancel,    //取消按钮回调
  void Function()? onCustom,    //自定义按钮回调
  Color? cancelTextColor,    //取消按钮文字颜色
  Color? confirmTextColor,    //确认按钮文字颜色
  String? textConfirm,    //确认按钮文字
  String? textCancel,    //取消按钮文字
  String? textCustom,    //自定义按钮文字
  Widget? confirm,    //确认按钮组件
  Widget? cancel,    //取消按钮组件
  Widget? custom,    //自定义按钮组件
  Color? backgroundColor,    //弹框背景色
  bool barrierDismissible = true,    //是否可以通过点击背景关闭弹框
  Color? buttonColor,    //按钮文字颜色
  String middleText = "Dialog made in 3 lines of code",    //内容区域显示的文字
  TextStyle? middleTextStyle,    //内容区域显示的文字的样式
  double radius = 20.0,    //弹框圆角
  List<Widget>? actions,    //增加额外子组件
  Future<bool> Function()? onWillPop,    //拦截关闭之前做的一些操作
  GlobalKey<NavigatorState>? navigatorKey,    //用于打开对话框的 Key
})

使用

///Getx 的使用

import 'package:flutter/material.dart';
import 'package:get/get.dart';//1.引入头文件

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const GetMaterialApp(    //2.更改 MaritalApp 为 GetMaterialApp
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  void showAlertDialog(context) async {
    var result = await showDialog(
        barrierDismissible: false, //表示点击灰色背景的时候是杳消失弹出框
        context: context,
        builder: (context) {
          return AlertDialog(
            title: const Text("标题"),
            content: const Text("内容"),
            actions: [
              TextButton(
                  onPressed: () {
                    // Navigator.popAndPushNamed(context, "OK");
                    Navigator.of(context).pop("确定");
                  },
                  child: const Text("确定")),
              TextButton(
                  onPressed: () {
                    // Navigator.popAndPushNamed(context, "取消");
                    Navigator.of(context).pop("取消");
                  },
                  child: const Text("取消")),
            ],
          );
        });
    print("----------");
    print(result);
  }

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Getx'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
                onPressed: () {
                  widget.showAlertDialog(context);
                },
                child: const Text("默认 Dialog")),
            ElevatedButton(
                onPressed: () {
                  Get.defaultDialog(
                      title: "标题",
                      // content: const Text("内容"),
                      middleText: "内容",
                      confirm: ElevatedButton(
                          onPressed: () {
                            print("确定");
                            Get.back();
                          },
                          child: const Text("确定")),
                      cancel: ElevatedButton(
                          onPressed: () {
                            print("取消");
                            Get.back();
                          },
                          child: const Text("取消")));
                },
                child: const Text("Getx 默认 Dialog")),
          ],
        ),
      ),
    );
  }
}
2、Get.snackbar

属性

SnackbarController snackbar(
  String title,    //弹框标题
  String message, {    //弹框消息
  Color? colorText,    //title 和 message 的文字颜色
  Duration? duration = const Duration(seconds: 3),    //弹框弹出的持续时间
  bool instantInit = true,    //为  false 可以将 snackbar 放在 initState
  SnackPosition? snackPosition,    //弹出位置,TOP 和 BOTTOM,默认 TOP
  Widget? titleText,    //标题组件,使用该属性会使 title 属性失效
  Widget? messageText,    //内容组件,使用该属性会使 message 属性失效
  Widget? icon,    //弹出时图标,显示在 title 和 message 左侧
  bool? shouldIconPulse,    //弹出时图标是否闪烁,默认 false
  double? maxWidth,    //Snackbar最大的宽度
  EdgeInsets? margin,    //Snackbar外边距,默认zero
  EdgeInsets? padding,    //Snackbar内边距,默认EdgeInsets.all(16)
  double? borderRadius,    //边框圆角大小,默认15
  Color? borderColor,    //边框的颜色,必须设置 borderWidth,否则无效果
  double? borderWidth,    //边框的线条宽度
  Color? backgroundColor,    //Snackbar背景颜色,默认Colors.grey.withOpacity(0.2)
  Color? leftBarIndicatorColor,    //左侧指示器的颜色
  List<BoxShadow>? boxShadows,    //Snackbar阴影颜色
  Gradient? backgroundGradient,    //背景的线性颜色
  TextButton? mainButton,    //主要按钮,一般显示发送、确认按钮
  void Function(GetSnackBar)? onTap,    //点击Snackbar事件回调
  bool? isDismissible,    //是否开启 Snackbar 手势关闭,可配合 dismissDirection 使用
  bool? showProgressIndicator,    //是否显示进度条指示器,默认false
  DismissDirection? dismissDirection,    //Snackbar关闭的方向
  AnimationController? progressIndicatorController,    //进度条指示器的动画控制器
  Color? progressIndicatorBackgroundColor,    //进度条指示器的背景颜色
  Animation<Color>? progressIndicatorValueColor,    //进度条指示器的背景颜色
  SnackStyle? snackStyle,    //Snackbar是否会附加到屏幕边缘
  Curve? forwardAnimationCurve,    //Snackbar 弹出的动画,默认Curves.easeOutCirc
  Curve? reverseAnimationCurve,    //Snackbar 消失的动画,默认Curves.easeOutCirc
  Duration? animationDuration,    //Snackbar弹出和小时的动画时长,默认1秒
  double? barBlur,    //Snackbar 背景的模糊度
  double? overlayBlur,    //弹出时的毛玻璃效果值,默认0
  void Function(SnackbarStatus?)? snackbarStatus,    //Snackbar 弹出或消失时的事件回调(即将打开、已打开、即将关闭、已关闭)
  Color? overlayColor,    //弹出时的毛玻璃的背景颜色
  Form? userInputForm,    //用户输入表单
})

使用

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Getx'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
                onPressed: () {
                  Get.snackbar("title", "message",
                      snackPosition: SnackPosition.BOTTOM);
                },
                child: const Text("Getx snackbar")),
          ],
        ),
      ),
    );
  }
}
3、Get.bottomSheet

属性

Future<T?> bottomSheet<T>(
  Widget bottomsheet, {    //弹出的Widget组件
  Color? backgroundColor,    //bottomsheet的背景颜色
  double? elevation,    //bottomsheet的阴影
  bool persistent = true,    //是否添加到路由中
  ShapeBorder? shape,    //边框形状,一般用于圆角效果
  Clip? clipBehavior,    //裁剪的方式
  Color? barrierColor,    //弹出层的背景颜色
  bool? ignoreSafeArea,    //是否忽略安全适配
  bool isScrollControlled = false,    //是否支持全屏弹出,默认false
  bool useRootNavigator = false,    //是否使用根导航
  bool isDismissible = true,    //点击背景是否可关闭,默认ture
  bool enableDrag = true,    //是否可以拖动关闭,默认true
  RouteSettings? settings,    //路由设置
  Duration? enterBottomSheetDuration,    //bottomsheet进入时的动画时间
  Duration? exitBottomSheetDuration,    //bottomsheet退出时的动画时间
})

使用

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Getx'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
                onPressed: () {
                  Get.bottomSheet(Container(
                    color: Get.isDarkMode ? Colors.black45 : Colors.white,
                    height: 250,
                    child: Column(
                      children: [
                        ListTile(
                          leading: Icon(Icons.wb_sunny_outlined,
                              color: Get.isDarkMode
                                  ? Colors.white54
                                  : Colors.black26), // Icon
                          title: Text(
                            "白天模式",
                            style: TextStyle(
                                color: Get.isDarkMode
                                    ? Colors.white54
                                    : Colors.black87),
                          ),
                          onTap: () {
                            Get.changeTheme(ThemeData.light());
                            Get.back();
                          },
                        ),
                        ListTile(
                          leading: Icon(Icons.wb_sunny,
                              color: Get.isDarkMode
                                  ? Colors.white54
                                  : Colors.black), // Icon
                          title: Text(
                            "夜晚模式",
                            style: TextStyle(
                                color: Get.isDarkMode
                                    ? Colors.white54
                                    : Colors.black),
                          ),
                          onTap: () {
                            Get.changeTheme(ThemeData.dark());
                            Get.back();
                          },
                        ),
                      ],
                    ),
                  ));
                },
                child: const Text("Getx bottomSheet 切换主题")),
          ],
        ),
      ),
    );
  }
}
4、嵌套

使用 Get 可以让 Flutter 的嵌套导航更加简单,它并不需要使用 context,而是通过 Id 找到导航栈。
注:通常情况下,不建议使用或尽量少用 NestedNavigators。若确实需要,要注意多个导航堆栈对内存的消耗。

Navigator(
    key: Get.nestedKey(1),
    initialRoute: '/',
    onGenerateRoute: (settings) {
        if (settings.name == '/') {
          return GetPageRoute(
              page: () => Scaffold(
                  appBar: AppBar(
                      title: Text("Main"),
                   ),
                   body: Center(
                       child: TextButton(
                          color: Colors.blue,
                          onPressed: () {
                              Get.toNamed('/second', id: 1);
                          },
                          child: Text("Go to second"),
                       ),
                     ),
                   ),
                 );
        } else if (settings.name == '/second') {
          return GetPageRoute(
              page: () => Center(
                   child: Scaffold(
                       appBar: AppBar(
                            title: Text("Main"),
                       ),
                     body: Center(child: Text("second")),
               ),
             ),
       );
   }
});

5、GetX 路由管理

GetX 为我们封装了 Navigation,无需 context 可进行跳转,使用 GetX 进行路由跳转非常的简单只需要调用Get.to()即可进行路由跳转,GetX 路由跳转简化了跳转动画设置、动画时长定义、动画曲线设置。

1、普通路由导航

1、导航到新的页面

Get.to(NextScreen());

2、关闭 SnackBars、Dialogs、BottomSheets 或任何你通常会用 Navigator.pop(context) 关闭的东西。

Get.back();

3、进入下一个页面,但没有返回上一个页面的选项

Get.off(NextScreen());

4、进入下一个界面并取消之前的所有路由

Get.offAll(NextScreen());

5、要导航到下一条路由,并在返回后立即接收或更新数据

var data = await Get.to(Payment());

6、在另一个页面上,发送前一个路由的数据并使用它

Get.back(result: 'success');
if(data == 'success') madeAnything();

7、与标准导航的关系 , 只要把 Navigator(大写)改成 navigator(小写),就可以拥有标准导航的所有功能,而不需要使用 context

// 默认的Flutter导航
Navigator.of(context).push(
  context,
  MaterialPageRoute(
    builder: (BuildContext context) {
      return HomePage();
    },
  ),
);
// 使用Flutter语法获得,而不需要context。
navigator.push(
  MaterialPageRoute(
    builder: (_) {
      return HomePage();
    },
  ),
);
// get语法
Get.to(HomePage());
2、别名路由导航

1、导航到下一个页面

Get.toNamed("/NextScreen");

2、浏览并删除前一个页面

Get.offNamed("/NextScreen");

3、浏览并删除所有以前的页面

Get.offAllNamed("/NextScreen");

4、要定义路由,使用 GetMaterialApp

void main() {
  runApp(GetMaterialApp(
    initialRoute: '/',
    getPages: [
      GetPage(name: '/', page: () => MyHomePage()),
      GetPage(name: '/second', page: () => Second()),
      GetPage(name: '/third', page: () => Third(), transition: Transition.zoom),
    ],
  ));
}

5、定义动画效果

void main() {
  runApp(GetMaterialApp(
    //全局动画效果
    defaultTransition: Transition.rightToLeftWithFade,
    //定义路由
    initialRoute: '/',
    getPages: [
      GetPage(name: '/', page: () => MyHomePage()),
      GetPage(
        name: '/second',
        page: () => Second(),
        //局部动画效果
        transition: Transition.rightToLeftWithFade,
      ),
      GetPage(name: '/third', page: () => Third(), transition: Transition.zoom),
    ],
  ));
}

6、处理到未定义路线的导航(404错误),可以在GetMaterialApp中定义unknownRoute页面。

void main() {
  runApp(GetMaterialApp(
    unknownRoute: GetPage(name: '/notfound', page: () => UnknownRoutePage()),
    initialRoute: '/',
    getPages: [
      GetPage(name: '/', page: () => MyHomePage()),
      GetPage(name: '/second', page: () => Second()),
    ],
  ));
}

7、发送数据到别名路由
只要发送你想要的参数即可。Get 在这里接受任何东西,无论是一个字符串,一个 Map,一个List,甚至一个类的实例。通过 Get.arguments 接受参数

Get.toNamed("/NextScreen", arguments: 'Get is the best');

类或控制器上:

print(Get.arguments);
//print out: Get is the best

8、动态网页链接
Get 提供高级动态 URL,就像在 Web上一样。Web 开发者可能已经在 Flutter 上想要这个功能了,Get 也解决了这个问题。

Get.offAllNamed("/NextScreen?device=phone&id=354&name=Enzo");

在 controller/bloc/stateful/stateless 类上:

print(Get.parameters['id']);
// out: 354
print(Get.parameters['name']);
// out: Enzo

也可以用 Get 轻松接收 NamedParameters

void main() {
  runApp(GetMaterialApp(
    initialRoute: '/',
    getPages: [
      GetPage(
        name: '/',
        page: () => MyHomePage(),
      ),
      GetPage(
        name: '/profile/',
        page: () => MyProfile(),
      ), //你可以为有参数的路由定义一个不同的页面,也可以为没有参数的路由定义一个不同的页面,但是你必须在不接收参数的路由上使用斜杠"/",就像上面那样。
      GetPage(
        name: '/profile/:user',
        page: () => UserProfile(),
      ),
      GetPage(
          name: '/third',
          page: () => Third(),
          transition: Transition.cupertino),
    ],
  ));
}

发送别名路由数据

Get.toNamed("/second/34954");

在第二个页面上,通过参数获取数据

print(Get.parameters['user']);
// out: 34954

发送多个参数

Get.toNamed("/profile/34954?flag=true");

在第二个屏幕上,通常按参数获取数据

print(Get.parameters['user']);
print(Get.parameters['flag']);
// out: 34954 true

可以使用 Get.toNamed() 来导航别名路由,不需要任何 context (可以直接从 BLoC 或 Controller 类中调用路由),当应用程序被编译到 web 时,路由将出现在URL中。

6、中间件

1、Middleware 介绍

中间件是介于应用系统和系统软件之间的一类软件,它使用系统软件所提供的基础服务,衔接网络上应用系统的各个部分或不同的应用,能够达到资源共享,功能共享的目的。

2、中间件配置
void main(List<String> args) {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      // home: HomePage(),
      getPages: [
        GetPage(
          name: "/home",
          page: () => HomePage(),
          middlewares: [GetMiddleware(priority: 1), GetMiddleware(priority: 2)]
        )
      ],
    );
  }
}

1、在 main.dart 中的 GetMaterialApp/getPages/GetPage 进行配置。
2、middlewares 是个数组,可配置多个,并且有优先级(优先级越低越先执行)。
3、middlewares 的配置,在要跳转的页面进行配置,不是跳转前页面配置。

3、定义 GetMiddleWare 过程
/*
    定义中间件
    1.继承自 GetMiddleware
    2.设置优先级(优先级越低越先执行)
    3.重写 GetMiddleware 方法
*/
class MyMiddleWare extends GetMiddleware {
  @override
  int get priority => -1;

  @override
  RouteSettings? redirect(String? route) {//重定向,当正在搜索被调用的路由时,将调用该函数
    // return super.redirect(route);
    return RouteSettings(name: AppRoutes.login);
  }

  @override
  GetPage? onPageCalled(GetPage? page) {//创建任何内容之前调用该函数
    // return super.onPageCalled(page);
    // return page?.copy(name: AppRoutes.login);
    return GetPage(name: AppRoutes.login, page: () => LoginWidge());
  }

  @override
  List<Bindings>? onBindingsStart(List<Bindings>? bindings) {//这个函数将在绑定初始化之前调用,在此可以更改此页面的绑定
    // return super.onBindingsStart(bindings);
    
    //添加操作...
    bindings?.add(LoginBiding());
    return bindings;
  }

  @override
  GetPageBuilder? onPageBuildStart(GetPageBuilder? page) {//此函数将在绑定初始化之后立即调用,在此可以在创建绑定之后和创建页面小部件之前执行操作
    // return super.onPageBuildStart(page);
    return page;
  }

  @override
  Widget onPageBuilt(Widget page) {//该函数将在 GetPage.page 之后立即调用,并提供函数的结果,并获取将显示的小部件
    // return super.onPageBuilt(page);
    return page;
  }

  @override
  void onPageDispose() {//在处理完页面的所有相关对象后立即调用
    super.onPageDispose();
  }
}
4、使用

1、如果想通过监听 Get 事件来触发动作,可以通过 routingCallback 来实现。

   return GetMaterialApp(routingCallback: (routing) {
      if (routing.current == '/second') {
        openAds();
      }
    });

2、如果没有使用 GetMaterialApp,可以使用手动 API 来附加 Middleware 观察器。

void main() {
  runApp(
    MaterialApp(
      onGenerateRoute: Router.generateRoute,
      initialRoute: "/",
      navigatorKey: Get.key,
      navigatorObservers: [
        GetObserver(MiddleWare.observer),//此处
      ],
    ),
  );
}

3、创建一个 MiddleWare 类

class MiddleWare {
  static observer(Routing routing) {
    ///除了可以监听路由外,还可以监听每个页面上的 SnackBars、Dialogs 、Bottomsheets。
    if (routing.current == '/second' && !routing.isSnackbar) {
      Get.snackbar("Hi", "You are on second route");
    } else if (routing.current == '/third') {
      print('last route called');
    }
  }
}

在代码上使用Get:

class First extends StatelessWidget {
  @overrideWidget
  build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        leading: IconButton(
          icon: Icon(Icons.add),
          onPressed: () {
            Get.snackbar("hi", "i am a modern snackbar");
          },
        ),
        title: Text('First Route'),
      ),
      body: Center(
        child: ElevatedButton(
          child: Text('Open route'),
          onPressed: () {
            Get.toNamed("/second");
          },
        ),
      ),
    );
  }
}

class Second extends StatelessWidget {
  @overrideWidget
  build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        leading: IconButton(
          icon: Icon(Icons.add),
          onPressed: () {
            Get.snackbar("hi", "i am a modern snackbar");
          },
        ),
        title: Text('second Route'),
      ),
      body: Center(
        child: ElevatedButton(
          child: Text('Open route'),
          onPressed: () {
            Get.toNamed("/third");
          },
        ),
      ),
    );
  }
}

class Third extends StatelessWidget {
  @overrideWidget
  build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Third Route"),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Get.back();
          },
          child: Text('Go back!'),
        ),
      ),
    );
  }
}

7、响应式

目前,Flutter 有几种状态管理器。但是,它们中的大多数都涉及到使用 ChangeNotifier 来更新 widget, 这对于中大型应用的性能来说是一个很糟糕的方法。
Get 是一个微框架,而不仅仅是一个状态管理器,既可以单独使用,也可以与其他状恋管理器結合使用。Get 有两个不同的状态管理器:响应式状态管理器、简单的状态管理器。

1、响应式状态管理器

使用示例

class HomePage extends StatelessWidget {
  //以 Int 为例
  final RxInt _counter = 0.obs; // RxInt(0);//也可以这么写
  /*
      其他类型
      final Rxstring _username = "zhangsan".obs; //RxString("zhangsan");
      final RxList _list = ["张三","李四"].obs;
      */
  HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
          child: const Icon(Icons.add),
          onPressed: () {
            _counter.value++;
          }),
      appBar: AppBar(
        title: const Text('Title'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Obx(() => Text(
                  "${_counter.value}",
                  style: const TextStyle(fontSize: 20, color: Colors.black),
                ))
          ],
        ),
      ),
    );
  }
}
2、简单的状态管理器
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容