深入理解 Flutter 布局约束原理


本文主要是针对flutter中布局约束原理的一个总结,以及对其29个展示样例做出分析。


flutter英文文档:https://docs.flutter.dev/ui/layout/constraints
flutter中文文档:https://docs.flutter.cn/ui/layout/constraints

我们会经常听到一些开发者在学习 Flutter 时的疑惑:为什么我设置了 width:100,但是看上去却不是 100 像素宽呢。(注意,本文中的“像素”均指的是逻辑像素)通常你会回答,将这个 Widget 放进 Center 中,对吧?

如果你这样做了,他们会不断找你询问这样的问题:为什么 FittedBox 又不起作用了?为什么 Column 又溢出边界,亦或是 IntrinsicWidth 应该做什么。

其实我们首先应该做的,是告诉他们 Flutter 的布局方式与 HTML 的布局差异相当大(这些开发者很可能是 Web 开发),然后要让他们熟记这条规则:

Constraints go down. Sizes go up. Parent sets position.

向下传递约束、向上传递尺寸、父widget决定子widget的位置。

  • 官方示例:


    image.png

那么谈判将会像这样:

Widget: "嘿!我的父级。我的约束是多少?"

Parent: "你的宽度必须在 0300 像素之间,高度必须在 085 之间。"

Widget: "嗯...我想要 5 个像素的内边距,这样我的子级能最多拥有 290 个像素宽度和 75 个像素高度。"

Widget: "嘿,我的第一个子级,你的宽度必须要在 0290,长度在 075 之间。"

First child: "OK,那我想要 290 像素的宽度,20 个像素的长度。"

Widget: "嗯...由于我想要将我的第二个子级放在第一个子级下面,所以我们仅剩 55 个像素的高度给第二个子级了。"

Widget: "嘿,我的第二个子级,你的宽度必须要在 0290,长度在 055 之间。"

Second child: "OK,那我想要 140 像素的宽度,30 个像素的长度。"

Widget: "很好。我的第一个子级将被放在 x: 5 & y: 5 的位置,而我的第二个子级将在 x: 80 & y: 25 的位置。"

Widget: "嘿,我的父级,我决定我的大小为 300 像素宽度,60 像素高度。"

首先了解两个概念

Terminology

When the minimum constraints and the maximum constraint in an axis are the same, that axis is tightly constrained.

An axis with a minimum constraint of 0.0 is loose (regardless of the maximum constraint; if it is also 0.0, then the axis is simultaneously tight and loose!).

原文地址: https://api.flutter.dev/flutter/rendering/BoxConstraints-class.html

严格约束 (Tight) 与宽松约束 (loose),我们通常也叫 紧约束、松约束。

严格约束:它的最大/最小宽度一样,最大/最小高度一样。

宽松约束:约束的最小宽度/高度为 0

//紧约束
BoxConstraints.tight(Size size)
  : minWidth = size.width,
    maxWidth = size.width,
    minHeight = size.height,
    maxHeight = size.height;
//松约束
BoxConstraints.loose(Size size)
  : minWidth = 0.0,
    maxWidth = size.width,
    minHeight = 0.0,
    maxHeight = size.height;

注意:松约束、紧约束并不是互斥的,当只要满足对应条件即可。比如当一个约束的最大最小约束均为0时,那么这个约束即是松约束,同时也是紧约束。

当父级widget是一个紧约束时,无论子widget设置宽高值时多少,都会强制子widget宽高为紧约束的值。

当父级widget是一个松约束时,当widget设置的宽高值在约束条件内时,宽高值生效。

获取和设置约束条件

  • 获取约束条件

    通过对widget包裹一层LayoutBuilder,可以获取父级对其约束条件,用法:

LayoutBuilder(
  builder: (BuildContext context, BoxConstraints constraints) {
    print("constraints = ${constraints}");//打印约束条件
    return Container(color: Colors.red);
  },
)

//可能输出结果:
//紧约束
flutter: constraints = BoxConstraints(w=393.0, h=852.0) 
//松约束
flutter: constraints = BoxConstraints(0.0<=w<=393.0, 0.0<=h<=852.0) 
// w:松约束  h:无限约束
flutter: constraints = BoxConstraints(0.0<=w<=393.0, 0.0<=h<=Infinity)


  • 设置约束条件

    在widget外面包裹ConstrainedBox,通过 constraints 属性进行设置,

ConstrainedBox(
  constraints: BoxConstraints(
    minWidth: 10,
    maxWidth: 100,
    minHeight: 10,
    maxHeight: 100,
  ),
  child: Container(color: Colors.red),
)

Container 源码

  • Container源码分析
class Container extends StatelessWidget {
  Container({
    super.key,
    this.alignment,
    this.padding,
    this.color,
    this.decoration,
    this.foregroundDecoration,
    double? width,
    double? height,
    BoxConstraints? constraints,
    this.margin,
    this.transform,
    this.transformAlignment,
    this.child,
    this.clipBehavior = Clip.none,
  }) : 
  //当宽或高不为空时,重新对constraints进行赋值
       constraints =
        (width != null || height != null)
          ? constraints?.tighten(width: width, height: height)
            ?? BoxConstraints.tightFor(width: width, height: height)
          : constraints;


  

  ......

  @override
  Widget build(BuildContext context) {
    Widget? current = child;
    //当child为空,约束为空或着是一个紧约束时,设置约束为BoxConstraints.expand()
    if (child == null && (constraints == null || !constraints!.isTight)) {
      current = LimitedBox(
        maxWidth: 0.0,
        maxHeight: 0.0,
        child: ConstrainedBox(constraints: const BoxConstraints.expand()),
      );
    } else if (alignment != null) {
      current = Align(alignment: alignment!, child: current);
    }

   ......

    //设置颜色
    if (color != null) {
      current = ColoredBox(color: color!, child: current);
    }
    
   ......

    //约束条件
    if (constraints != null) {
      current = ConstrainedBox(constraints: constraints!, child: current);
    }

    ......

    return current!;
  }
}
  • BoxConstraints.expand
const BoxConstraints.expand({
  double? width,
  double? height,
}) : minWidth = width ?? double.infinity,
     maxWidth = width ?? double.infinity,
     minHeight = height ?? double.infinity,
     maxHeight = height ?? double.infinity;

样例分析

  • 样例1
Container(color: Colors.red)

整个屏幕(root)作为 Container 的父级,并且强制 Container 变成和屏幕一样的大小(紧约束)。

所以这个 Container 充满了整个屏幕,并绘制成红色。

本质原因:
因为Container的父级对其是一个紧约束。强制要求width、height位屏幕尺寸大小。可以通过下面样例1数结构图可以知,也可在Container外面包裹LayoutBuilder后打印约束条件。

image.png
image.png
  • 样例2
Container(
  width: 100,
  height: 100,
  color: Colors.red,
)

红色的 Container 想要变成 100 x 100 的大小,但是它无法变成,因为屏幕强制它变成和屏幕一样的大小(紧约束)。

所以 Container 充满了整个屏幕。

image.png
image.png
  • 样例3
Center(
  child: Container(width: 100, height: 100, color: Colors.red),
)

屏幕强制 Center 变得和屏幕一样大,所以 Center 充满了屏幕。

然后 Center 告诉 Container 可以变成任意大小(松约束),但是不能超出屏幕。现在,Container 可以真正变成 100 × 100 大小了。

image.png
image.png
  • 样例4
 Align(
  alignment: Alignment.bottomRight,
  child: Container(width: 100, height: 100, color: Colors.red),
)

与上一个样例不同的是,我们使用了 Align 而不是 Center

Align 同样也告诉 Container,你可以变成任意大小(松约束)。但是,如果还留有空白空间的话,它不会居中 Container。相反,它将会在允许的空间内,把 Container 放在右下角(bottomRight)。

image.png

[图片上传失败...(image-3e78ee-1721805129145)]

  • 样例5
 Center(
  child: Container(
      width: double.infinity, height: double.infinity, color: Colors.red),
)

屏幕强制 Center 变得和屏幕一样大,所以 Center 充满屏幕。

然后 Center 告诉 Container 可以变成任意大小(松约束),但是不能超出屏幕。现在,Container 想要无限的大小,但是由于它不能比屏幕更大,所以就仅充满屏幕。

image.png

[图片上传失败...(image-d0345a-1721805129145)]

  • 样例6
Center(
  child: Container(color: Colors.red),
)

屏幕强制 Center 变得和屏幕一样大,所以 Center 充满屏幕。

然后 Center 告诉 Container 可以变成任意大小,但是不能超出屏幕。由于 Container 没有子级而且没有固定大小,所以它决定能有多大就有多大,所以它充满了整个屏幕。

因为Container源码中,如果child == null 且 constraints == null时,设置了其约束为BoxConstraints.expand()

image.png
image.png
  • 样例7
Center(
  child: Container(
    color: Colors.red,
    child: Container(color: Colors.green, width: 30, height: 30),
  ),
)

屏幕强制 Center 变得和屏幕一样大,所以 Center 充满屏幕。

然后 Center 告诉红色的 Container 可以变成任意大小,但是不能超出屏幕。由于 Container 没有固定大小但是有子级,所以它决定变成它 child 的大小。

然后红色的 Container 告诉它的 child 可以变成任意大小,但是不能超出屏幕。

而它的 child 是一个想要 30 × 30 大小绿色的 Container。由于红色的 Container 和其子级一样大,所以也变为 30 × 30。由于绿色的 Container 完全覆盖了红色 Container,所以你看不见它了。

image.png
image.png
  • 样例8
Center(
  child: Container(
    padding: const EdgeInsets.all(20),
    color: Colors.red,
    child: Container(color: Colors.green, width: 30, height: 30),
  ),
)

红色 Container 变为其子级的大小,但是它将其 padding 带入了约束的计算中。所以它有一个 20 x 20 的外边距。由于这个外边距,所以现在你能看见红色了。而绿色的 Container 则还是和之前一样。

image.png

[图片上传失败...(image-3c2d30-1721805129145)]

  • 样例9
ConstrainedBox(
  constraints: const BoxConstraints(
    minWidth: 70,
    minHeight: 70,
    maxWidth: 150,
    maxHeight: 150,
  ),
  child: Container(color: Colors.red, width: 10, height: 10),
)

你可能会猜想 Container 的尺寸会在 70 到 150 像素之间,但并不是这样。 ConstrainedBox 仅对其从其父级接收到的约束下施加其他约束。

在这里,root 对 ConstrainedBox 是一个紧约束,使其与屏幕大小完全相同,因此它告诉其子 Widget 也以屏幕大小作为约束,从而忽略了其 constraints 参数带来的影响。

image.png

[图片上传失败...(image-6d1963-1721805129145)]

  • 样例10
Center(
  child: ConstrainedBox(
    constraints: const BoxConstraints(
      minWidth: 70,
      minHeight: 70,
      maxWidth: 150,
      maxHeight: 150,
    ),
    child: Container(color: Colors.red, width: 10, height: 10),
  ),
)

外部添加 Center 后,CenterConstrainedBox的约束变成了松约束,这样对ConstrainedBox设置的约束满足父级的约束,ConstrainedBoxconstraints 参数带来的约束附加到其子对象上。
ConstrainedBoxContainer 约束,要求 Container 必须介于 70 到 150 像素之间。虽然它希望自己有 10 个像素大小,但最终获得了 70 个像素(最小为 70)。

image.png
image.png
  • 样例11
Center(
  child: ConstrainedBox(
    constraints: const BoxConstraints(
      minWidth: 70,
      minHeight: 70,
      maxWidth: 150,
      maxHeight: 150,
    ),
    child: Container(color: Colors.red, width: 1000, height: 1000),
  ),
)

同样例10

  • 样例12
Center(
  child: ConstrainedBox(
    constraints: const BoxConstraints(
      minWidth: 70,
      minHeight: 70,
      maxWidth: 150,
      maxHeight: 150,
    ),
    child: Container(color: Colors.red, width: 100, height: 100),
  ),
)

同样例10

  • 样例13
UnconstrainedBox(
  child: Container(color: Colors.red, width: 20, height: 50),
)

屏幕强制 UnconstrainedBox 变得和屏幕一样大,而 UnconstrainedBox 允许其子级的 Container 可以变为任意大小,对子级的约束条件是无约束 BoxConstraints(unconstrained)

他是一个松约束(BoxConstraints( minWidth: 0, minHeight: 0, maxWidth: Infinity, maxHeight: Infinity))

image.png

[图片上传失败...(image-af1583-1721805129145)]

  • 样例14
UnconstrainedBox(
  child: Container(color: Colors.red, width: 4000, height: 50),
)

屏幕强制 UnconstrainedBox 变得和屏幕一样大,而 UnconstrainedBox 允许其子级的 Container 可以变为任意大小。

不幸的是,在这种情况下,容器的宽度为 4000 像素,这实在是太大,以至于无法容纳在 UnconstrainedBox 中,因此 UnconstrainedBox 将显示溢出警告(overflow warning)。

image.png

[图片上传失败...(image-517937-1721805129145)]

  • 样例15
OverflowBox(
  minWidth: 0,
  minHeight: 0,
  maxWidth: double.infinity,
  maxHeight: double.infinity,
  child: Container(color: Colors.red, width: 4000, height: 50),
)

屏幕强制 OverflowBox 变得和屏幕一样大,并且 OverflowBox 允许其子容器设置为任意大小。

OverflowBoxUnconstrainedBox 类似,但不同的是,如果其子级超出该空间,它将不会显示任何警告。

在这种情况下,容器的宽度为 4000 像素,并且太大而无法容纳在 OverflowBox 中,但是 OverflowBox 会全部显示,而不会发出警告。

image.png

[图片上传失败...(image-c61c4e-1721805129145)]

  • 样例16
UnconstrainedBox(
  child: Container(color: Colors.red, width: double.infinity, height: 100),
)

这将不会渲染任何东西,而且你能在控制台看到错误信息。

UnconstrainedBox 让它的子级决定成为任何大小,但是其子级是一个具有无限大小的 Container

Flutter 无法渲染无限大的东西,所以它抛出以下错误: BoxConstraints forces an infinite width.(盒子约束强制使用了无限的宽度)

image.png

[图片上传失败...(image-bbe9f2-1721805129145)]

  • 样例17
UnconstrainedBox(
  child: LimitedBox(
    maxWidth: 100,
    child: Container(
      color: Colors.red,
      width: double.infinity,
      height: 100,
    ),
  ),
)

这次你就不会遇到报错了。 UnconstrainedBoxLimitedBox 一个无限的大小;但它向其子级传递了最大为 100 的约束。

如果你将 UnconstrainedBox 替换为 Center,则LimitedBox 将不再应用其限制(因为其限制仅在获得无限约束时才适用),并且容器的宽度允许超过 100。

上面的样例解释了 LimitedBoxConstrainedBox 之间的区别。

image.png

[图片上传失败...(image-178c6-1721805129145)]

  • 样例18
const FittedBox(
  child: Text(
    'Some Example Text.',
    textDirection: TextDirection.ltr,
  ),
)

屏幕强制 FittedBox 变得和屏幕一样大,而 Text 则是有一个自然宽度(也被称作 intrinsic 宽度),它取决于文本数量,字体大小等因素。

FittedBoxText 可以变为任意大小。但是在 Text 告诉 FittedBox 其大小后, FittedBox 缩放文本直到填满所有可用宽度。

主要原因,FittedBox对子级widget的约束是无限制约束,当子级widget渲染后尺寸,不满足FittedBox的约束条件时,就会产生缩放。

image.png
image.png
  • 样例19
const Center(
  child: FittedBox(
    child: Text(
      'Some Example Text.',
      textDirection: TextDirection.ltr,
    ),
  ),
)

但如果你将 FittedBox 放进 Center widget 中会发生什么? Center 将会让 FittedBox 能够变为任意大小,取决于屏幕大小。

FittedBox 然后会根据 Text 调整自己的大小,然后让 Text 可以变为所需的任意大小,由于二者具有同一大小,因此不会发生缩放。

image.png

[图片上传失败...(image-34a04f-1721805129145)]

  • 样例20
 const Center(
  child: FittedBox(
    child: Text(
      'This is some very very very large text that is too big to fit a regular screen in a single line.',
      textDirection: TextDirection.ltr,
    ),
  ),
)

然而,如果 FittedBox 位于 Center 中,但 Text 太大而超出屏幕,会发生什么?

FittedBox 会尝试根据 Text 大小调整大小,但不能大于屏幕大小。然后假定屏幕大小,并调整 Text 的大小以使其也适合屏幕。

image.png
image.png
  • 样例21
 const Center(
  child: Text(
      textDirection: TextDirection.ltr,
      'This is some very very very large text that is too big to fit a regular screen in a single line.'),
)

然而,如果你删除了 FittedBox, Text 则会从屏幕上获取其最大宽度,并在合适的地方换行。

image.png

[图片上传失败...(image-5cbd7f-1721805129145)]

  • 样例22
FittedBox(
  child: Container(
    height: 20,
    width: double.infinity,
    color: Colors.red,
  ),
)

FittedBox 只能在有限制的宽高中对子 widget 进行缩放(宽度和高度不会变得无限大)。否则,它将无法渲染任何内容,并且你会在控制台中看到错误。

image.png

[图片上传失败...(image-df78a0-1721805129145)]

  • 样例23
Directionality(
  textDirection: TextDirection.ltr,
  child: Row(
    children: [
      Container(
        color: Colors.red,
        child: const Text(
          'Hello!',
          style: TextStyle(fontWeight: FontWeight.bold),
        ),
      ),
      Container(
        color: Colors.green,
        child: const Text(
          'Goodbye!',
          style: TextStyle(fontWeight: FontWeight.bold),
        ),
      ),
    ],
  ),
)

屏幕强制 Row 变得和屏幕一样大,所以 Row 充满屏幕。

UnconstrainedBox 一样, Row 也不会对其子代施加任何约束,而是让它们成为所需的任意大小。 Row 然后将它们并排放置,任何多余的空间都将保持空白。

image.png

[图片上传失败...(image-7acd51-1721805129145)]

  • 样例24
Directionality(
  textDirection: TextDirection.ltr,
  child: Row(
    children: [
      Container(
        color: Colors.red,
        child: const Text(
          'This is a very long text that '
          'This is a very long text that '
          'won't fit the line.',
          style: TextStyle(fontWeight: FontWeight.bold),
        ),
      ),
      Container(
          color: Colors.green,
          child: const Text(
            'Goodbye!',
            style: TextStyle(fontWeight: FontWeight.bold),
          )),
    ],
  ),
)

由于 Row 不会对其子级施加任何约束,并不是不施加约束,而是对子级宽度不限制,高度为父级对其约束的高度。

因此它的 children 很有可能太大而超出 Row 的可用宽度。在这种情况下, Row 会和 UnconstrainedBox 一样显示溢出警告。

image.png

[图片上传失败...(image-9fe202-1721805129145)]

  • 样例25
Directionality(
  textDirection: TextDirection.ltr,
  child: Row(
    children: [
      Expanded(
        child: Center(
          child: Container(
            color: Colors.red,
            child: const Text(
              'This is a very long text that won't fit the line.'
              'This is a very long text that won't fit the line.',
              style: TextStyle(fontWeight: FontWeight.bold),
            ),
          ),
        ),
      ),
      Container(
        color: Colors.green,
        child: const Text(
          'Goodbye!',
          style: TextStyle(fontWeight: FontWeight.bold),
        ),
      ),
    ],
  ),
)

Row 的子级被包裹在了 Expanded widget 之后, Row 将不会再让其决定自身的宽度了。

取而代之的是,Row 会根据所有 Expanded 的子级来计算其该有的宽度。

换句话说,一旦你使用 Expanded,子级自身的宽度就变得无关紧要,直接会被忽略掉。
这是因为 ExpandedFlexFit.tight ,是一个紧约束,其实这里有些特殊,这里是仅限制宽度是一个紧约束,而高度是一个松约束。通过下图中 widget Details Tree 可以看出

class Expanded extends Flexible {
  /// Creates a widget that expands a child of a [Row], [Column], or [Flex]
  /// so that the child fills the available space along the flex widget's
  /// main axis.
  const Expanded({
    super.key,
    super.flex,
    required super.child,
  }) : super(fit: FlexFit.tight);
}
image.png

[图片上传失败...(image-e43482-1721805129145)]

  • 样例26
Directionality(
  textDirection: TextDirection.ltr,
  child: Row(
    children: [
      Expanded(
        child: Container(
          color: Colors.red,
          child: const Text(
            'This is a very long text that won't fit the line.',
            style: TextStyle(fontWeight: FontWeight.bold),
          ),
        ),
      ),
      Expanded(
        child: Container(
          color: Colors.green,
          child: const Text(
            'Goodbye!',
            style: TextStyle(fontWeight: FontWeight.bold),
          ),
        ),
      ),
    ],
  ),
)

如果所有 Row 的子级都被包裹了 Expanded widget,每一个 Expanded 大小都会与其 flex 因子成比例,并且 Expanded widget 将会强制其子级具有与 Expanded 相同的宽度。

换句话说,Expanded 忽略了其子 Widget 想要的宽度。

image.png

[图片上传失败...(image-152446-1721805129145)]

  • 样例27
Directionality(
  textDirection: TextDirection.ltr,
  child: Row(
    children: [
      Flexible(
        child: Container(
          color: Colors.red,
          child: const Text(
            'This is a very long text that won't fit the line.',
            style: TextStyle(fontWeight: FontWeight.bold),
          ),
        ),
      ),
      Flexible(
        child: Container(
          color: Colors.green,
          child: const Text(
            'Goodbye!',
            style: TextStyle(fontWeight: FontWeight.bold),
          ),
        ),
      ),
    ],
  ),
)

如果你使用 Flexible 而不是 Expanded 的话,唯一的区别是,Flexible 会让其子级具有与 Flexible 相同或者更小的宽度。而 Expanded 将会强制其子级具有和 Expanded 相同的宽度。但无论是 Expanded 还是 Flexible 在它们决定子级大小时都会忽略其宽度。

Flexible: 默认传入的 fit = FlexFit.loose 对其子级是一个松约束,子级大小会根据他的子级内容大小进行自适应、

Expanded: 继承自Flexible,传入的 fit = FlexFit.tight 对其子级在宽度上是一个紧约束,会强制子级宽度与其相等

image.png

[图片上传失败...(image-cbfaf8-1721805129145)]

  • 样例28
MaterialApp(
  home: Scaffold(
    body: Container(
      color: Colors.blue,
      child: const Column(
        children: [
          SizedBox(height: 100),
          Text(
            'Hello!',
            style: TextStyle(fontWeight: FontWeight.bold),
          ),
          Text(
            'Goodbye!',
            style: TextStyle(fontWeight: FontWeight.bold),
          ),
        ],
      ),
    ),
  ),
)

屏幕强制 Scaffold 变得和屏幕一样大,所以 Scaffold 充满屏幕。然后 Scaffold 告诉 Container 可以变为任意大小,但不能超出屏幕。

image.png

[图片上传失败...(image-b8a8cc-1721805129145)]

  • 样例29
 MaterialApp(
  home: Scaffold(
    body: SizedBox.expand(
      child: Container(
        color: Colors.blue,
        child: const Column(
          children: [
            SizedBox(height: 100),
            Text(
              'Hello!',
              style: TextStyle(fontWeight: FontWeight.bold),
            ),
            Text(
              'Goodbye!',
              style: TextStyle(fontWeight: FontWeight.bold),
            ),
          ],
        ),
      ),
    ),
  ),
)
image.png

[图片上传失败...(image-6d3802-1721805129145)]

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350