Flutter中自定义一个Controller控制器

我们在做项目开发的时候,经常发现有些组件需要我们传递一个controller,通过这个controller 来控制当前组件的行为例如 通过 ScrollController 来监听可滚动组件的滚动距离、控制滑动距离,使用TextEditingController 来控制TextField 处理输入框的文本信息,达到在页面内控制组件状态 的行为,这个控制器 controller 到底是什么东西,是如何来做到没有调用setState 就达到页面刷新的效果呢,我们进入到 ListView 内部看一下,发现这个ScrollController 是一个继承于ChangeNotifier的类

从字面意思我们就可以了解到即'变化 通知',就是有了变化就会发出一个通知,我们收到这个通知,做一些页面UI的刷新,具体是如何做到的呢,下面我们自己定义个组件,并通过自定义一个controller 来详细介绍一下 ChangeNotifier 的使用方式。

1、 自定义一个卡片组件

代码很简单,就是一个Container 放到页面中:

scala 复制代码
class CustomCard extends StatelessWidget {
  const CustomCard({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
          color: Colors.red[100], borderRadius: BorderRadius.circular(8)),
      width: 100,
      height: 100,
      alignment: Alignment.center,
      child: const Text('Card'),
    );
  }
}

2、下面开始,把组件中的宽高、颜色、文字抽取到外面,并在页面中定义一个按钮改变CustomCard 的状态:

less 复制代码
Column(
  crossAxisAlignment: CrossAxisAlignment.center,
  mainAxisAlignment: MainAxisAlignment.center,
  children: [

    CustomCard(width: _width, color: _color, text: _text),

    ElevatedButton(onPressed: (){
      setState(() {
        _width += 10;
        _color = Colors.primaries[ Random().nextInt( Colors.primaries.length)];
        _text = Random().nextInt(100).toString();
      });
    }, child: const Text('Change'))

  ],
),
arduino 复制代码
class CustomCard extends StatelessWidget {

  double ? width;
  Color ? color;
  String ? text;
  CustomCard({Key? key, this.width, this.color, this.text}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
          color: color ?? Colors.red[100], borderRadius: BorderRadius.circular(8)),
      width: width ?? 100,
      height: width ??100,
      alignment: Alignment.center,
      child: Text(text??''),
    );
  }
}

点击Change 按钮,卡片放大,更换随机背景色,更改text 文本,这时候还没有我们想要的控制器,别着急,我们慢慢来。

3、定义一个Controller控制器:

ini 复制代码
class CustomCardController extends ChangeNotifier {
  double? _width = 100;
  Color? _color = Colors.red.withOpacity(0.5);
  String? _text = 'Card';

  double get width => _width ?? 0;

  set width(newVal) {
    _width = newVal;
    notifyListeners();
  }

  Color get color => _color ?? Colors.white;

  set color(newVal) {
    _color = newVal;
    notifyListeners();
  }

  String get text => _text ?? '';

  set text(newVal) {
    _text = newVal;
    notifyListeners();
  }
}

这里面改变属性的时候我们用到了 set 方法,并且在set 方法内调用了notifListeners 方法,这是很重要的一点,在属性值改变时必须调用notifyListeners,并且在组件内需要配合使用 ListenableBuilder (在旧版本的Flutter中使用AnimatedBuilder,属性是animation,这两个仅仅是名称和属性名不同,用法是一模一样的) 来触发更新:

页面组件:

less 复制代码
Column(
  crossAxisAlignment: CrossAxisAlignment.center,
  mainAxisAlignment: MainAxisAlignment.center,
  children: [
    
    CustomCard(controller: controller),
    Padding(
      padding: const EdgeInsets.only(top: 20),
      child: ElevatedButton(
        onPressed: () {
          controller.width += 10;
        },
        child: const Text('change width'),
      ),
    ),
    ElevatedButton(
        onPressed: () {
          controller.color = Colors
              .primaries[Random().nextInt(Colors.primaries.length)];
        },
        child: const Text('change color')),
    ElevatedButton(
        onPressed: () {
          controller.text =
              controller.text + controller.text.length.toString();
        },
        child: const Text('change text'))
  ],
)

自定义卡片组件:

scala 复制代码
class CustomCard extends StatelessWidget {
  CustomCard({Key? key, required this.controller}) : super(key: key);
  CustomCardController controller = CustomCardController();

  @override
  Widget build(BuildContext context) {
    return ListenableBuilder(
      listenable: controller,
      builder: (BuildContext context, Widget? child) {
        return Container(
          width: controller.width,
          height: controller.width,
          color: controller.color,
          alignment: Alignment.center,
          child: Text(controller.text),
        );
      },
    );
  }
}

使用自定义的Controller后,在页面中通过controller 操作我们的卡片组件,并且没有调用setState 就达到的刷新页面的效果,这在很大程度上能提升App的性能。

4、Controller 的优化

我们在定义一个controler 时,内部使用了很多get set 方法,并且每个set 方法内都调用了一次 notifiyListeners, 这样看起来不够优雅,实际上Flutter内已经封装好一个类,实现了监听一个值的改变并且自动调用notifyListeners 方法那就是ValueNotifier,下面我们把Controller 改造一下:

scss 复制代码
class CustomCardController {

  ValueNotifier<double> width = ValueNotifier(100);
  ValueNotifier<Color> color = ValueNotifier(Colors.red[100]!);
  ValueNotifier<String> text = ValueNotifier('Card');
}

我们的自定义卡片也需要做一下改变:

scala 复制代码
class CustomCard extends StatelessWidget {
  CustomCard({Key? key, required this.controller}) : super(key: key);
  CustomCardController controller = CustomCardController();

  @override
  Widget build(BuildContext context) {
    return ListenableBuilder(
      listenable: Listenable.merge([controller.width, controller.color, controller.text]),
      builder: (BuildContext context, Widget? child) {
        return Container(
          width: controller.width.value,
          height: controller.width.value,
          color: controller.color.value,
          alignment: Alignment.center,
          child: Text(controller.text.value),
        );
      },
    );
  }
}

由于我们监听的是三个值的变化,所以在listenable 属性这里需要使用 Listenable.merge

页面组件也需要改动一下:

less 复制代码
Column(
  crossAxisAlignment: CrossAxisAlignment.center,
  mainAxisAlignment: MainAxisAlignment.center,
  children: [

    CustomCard(controller: controller),
    Padding(
      padding: const EdgeInsets.only(top: 20),
      child: ElevatedButton(
        onPressed: () {
          controller.width.value += 10;
        },
        child: const Text('change width'),
      ),
    ),
    ElevatedButton(
        onPressed: () {
          controller.color.value = Colors
              .primaries[Random().nextInt(Colors.primaries.length)];
        },
        child: const Text('change color')),
    ElevatedButton(
        onPressed: () {
          controller.text.value =
              controller.text.value + controller.text.value.length.toString();
        },
        child: const Text('change text'))
  ],
)

到此,我们自定义的一个Controller 就完成了。

相关推荐
君蓦8 小时前
Flutter 本地存储与数据库的使用和优化
flutter
problc18 小时前
Flutter中文字体设置指南:打造个性化的应用体验
android·javascript·flutter
lqj_本人1 天前
鸿蒙next选择 Flutter 开发跨平台应用的原因
flutter·华为·harmonyos
lqj_本人1 天前
Flutter&鸿蒙next 状态管理框架对比分析
flutter·华为·harmonyos
起司锅仔1 天前
Flutter启动流程(2)
flutter
hello world smile2 天前
最全的Flutter中pubspec.yaml及其yaml 语法的使用说明
android·前端·javascript·flutter·dart·yaml·pubspec.yaml
lqj_本人2 天前
Flutter 的 Widget 概述与常用 Widgets 与鸿蒙 Next 的对比
flutter·harmonyos
iFlyCai2 天前
极简实现酷炫动效:Flutter隐式动画指南第二篇之一些酷炫的隐式动画效果
flutter
lqj_本人2 天前
Flutter&鸿蒙next 中使用 MobX 进行状态管理
flutter·华为·harmonyos