Flutter不常用组件(五)

228 阅读4分钟

PhysicalModel

为子组件设置阴影

它有以下几个属性:

  • Key? key:标识键
  • BoxShape shape:形状。默认为BoxShape.rectangle
  • Clip clipBehavior:多余部分裁剪效果。默认为Clip.none
  • BorderRadius? borderRadius:圆角
  • double elevation:相对于放置此物理对象的父级的 z 坐标。默认为0.0
  • Color color:颜色
  • Color shadowColor:阴影颜色。默认为Color(0xFF000000)
  • Widget? child:子组件
Center(
  child: PhysicalModel(
    shape: BoxShape.rectangle,
    clipBehavior: Clip.none,
    borderRadius: BorderRadius.all(Radius.circular(12)),
    color: Colors.white,
    elevation: 4.0,
    shadowColor: Color(0xFF000000),
    child: SizedBox(width: 200, height: 200),
  ),
)

image

PhysicalShape

创建具有任意形状剪辑的组件

它有以下几个属性:

  • Key? key:标识键
  • CustomClipper clipper:裁剪形状。可以通过继承CustomClipper自定义
  • Clip clipBehavior:多余部分裁剪效果。默认为Clip.none
  • double elevation:相对于放置此物理对象的父级的 z 坐标。默认为0.0
  • Color color:颜色
  • Color shadowColor:阴影颜色。默认为Color(0xFF000000)
  • Widget? child:子组件
Center(
  child: PhysicalShape(
    elevation: 5.0,
    clipper: ShapeBorderClipper(
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(100.0),
      ),
    ),
    color: Colors.orange,
    child: Container(
      height: 200.0,
      width: 200.0,
      alignment: Alignment.center,
      child: const Text(
        'Hello, World!',
        style: TextStyle(color: Colors.white, fontSize: 20.0),
      ),
    ),
  ),
)

image

PlatformAdaptiveIcons

使用Icons.adaptive开头的图标,可以在 Android 和 IOS 两个平台显示不同的图标样式。

RotatedBox

旋转子组件。旋转角度有限

它有以下几个属性:

  • Key? key:标识键
  • int quarterTurns:旋转角度。1代表90°,以此类推
  • Widget? child:子组件
Center(
  child: RotatedBox(
    quarterTurns: 1,
    child: FlutterLogo(size: 200),
  ),
)

image

SearchDelegate

定义搜索页面

我们先制作一个前往搜索页面的组件:

Center(
  child: Container(
    margin: const EdgeInsets.symmetric(horizontal: 16),
    child: TextField(
      readOnly: true,
      decoration: const InputDecoration(
        hintText: '搜索',
        suffixIcon: Icon(Icons.search),
      ),
      onTap: () {},  // TODO
    ),
  ),
)

image

SearchDelegate不能直接使用,我们需要自定义一类来继承它。

class SearchPage extends SearchDelegate<String> {
  @override
  List<Widget>? buildActions(BuildContext context) {
    return [
      IconButton(
        onPressed: () {
          query = '';
        },
        icon: const Icon(Icons.clear),
      ),
    ];
  }

  @override
  Widget? buildLeading(BuildContext context) {
    return const BackButton();
  }

  @override
  Widget buildResults(BuildContext context) {
    return searchContent();
  }

  @override
  Widget buildSuggestions(BuildContext context) {
    return searchContent();
  }

  Widget searchContent() {
    List<String> filterName = names
        .where((name) => name.toLowerCase().contains(query.toLowerCase()))
        .toList();
    return ListView.builder(
      itemCount: filterName.length,
      itemBuilder: (context, index) {
        return ListTile(
          title: Text(filterName[index]),
          onTap: () {
            query = filterName[index];
          },
        );
      },
    );
  }
}

接下来我们需要调用showSearch这个方法来显示这个界面:

onTap: () {
	showSearch(context: context, delegate: SearchPage());
}

image

ShaderMask

创建一个将Shader生成的蒙版应用到其子级的小部件

它有以下几个属性:

  • Key? key:标识键
  • Shader Function(Rect) shaderCallback:调用以创建生成遮罩的dart:ui.Shader
  • BlendMode blendMode:平铺模式。默认值为BlendMode.modulate
  • Widget? child:子组件
ShaderMask(
  shaderCallback: (bounds) => const RadialGradient(
    center: Alignment.topLeft,
    radius: 1.0,
    colors: [Colors.yellow, Colors.deepOrange],
    tileMode: TileMode.mirror,
  ).createShader(bounds),
  child: const Text(
    "Hello World",
    style: TextStyle(
      fontSize: 40,
      color: Colors.white,  // 一定要设置为白色
      fontWeight: FontWeight.bold,
    ),
  ),
)

image

SizedOverflowBox

关于该组件的详细介绍可以查看文章:SizedOverflowBox

Stepper

步骤组件

它有以下几个组件:

  • Key? key:标识键
  • List stepsStep列表
  • ScrollPhysics? physics:物理滚动效果
  • StepperType type:步骤指示条显示的方式。默认为StepperType.vertical
  • int currentStep:当前步骤。默认为0
  • void Function(int)? onStepTapped:步骤点击事件
  • void Function()? onStepContinue:步骤继续事件
  • void Function()? onStepCancel:步骤取消事件
  • Widget Function(BuildContext ControlsDetails)? controlsBuilder:创建自定义控件
  • double? elevation:相对于放置此物理对象的父级的 z 坐标。默认为0.0
  • EdgeInsetsGeometry? margin:外边距
Stepper(
  currentStep: _index,
  onStepCancel: () {
    if (_index > 0) {
      setState(() {
        _index -= 1;
      });
    }
  },
  onStepContinue: () {
    if (_index <= 0) {
      setState(() {
        _index += 1;
      });
    }
  },
  onStepTapped: (int index) {
    setState(() {
      _index = index;
    });
  },
  steps: <Step>[
    Step(
      title: const Text('步骤 1'),
      content: Container(
        alignment: Alignment.centerLeft,
        child: const Text('确认是否继续?'),
      ),
    ),
    const Step(
      title: Text('步骤 2'),
      content: Text('确定还继续?'),
    ),
  ],
)

image

UnconstrainedBox

用于去除父组件的约束

它有以下几个属性:

  • Key? key:标识键
  • Widget? child:子组件
  • TextDirection? textDirection:文本方向
  • AlignmentGeometry alignment:对齐方式。默认为Alignment.center
  • Axis? constrainedAxis:保留约束的轴
  • Clip clipBehavior :裁剪效果。默认为Clip.none
UnconstrainedBox(
  constrainedAxis: Axis.vertical,
  child: Container(
    color: Colors.blue,
    width: 150,
  ),
)

image

如果我们不使用UnconstrainedBox,直接写Container(color: Colors.blue)不写宽高,会默认占满整个界面。当我们使用UnconstrainedBox后,Container不写宽高就不会显示任何东西,这里Axis.vertical是保留垂直方向的约束,所以高度为这个界面,如果Container不写宽度,照样什么都看不到。这个演示代码去掉UnconstrainedBox效果也是第二张图,这里只做该组件的讲解。

WillPopScope

创建一个小部件,该小部件注册回调以否决用户尝试关闭封闭的ModalRoute

它有以下几个属性:

  • Key? key:标识键
  • Widget child:子组件
  • Future Function()? onWillPop:关闭封闭的ModalRoute事件
WillPopScope(
  child: Scaffold(
      appBar: AppBar(title: const Text("TestWidget")),
      body: const Center(
        child: Text("双击返回键退出"),
      )),
  onWillPop: () async {
    if (_lastQUitTime == null || DateTime.now().difference(_lastQUitTime!).inMilliseconds > 500) {
      _lastQUitTime = DateTime.now();
      return false;
    } else {
      Navigator.of(context).pop(true);
      // await SystemChannels.platform.invokeMethod('SystemNavigator.pop');
      return true;
    }
  },
)

image